Skip to content

Commit

Permalink
Cox Okta 429 retry PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-wfateem committed Dec 10, 2024
1 parent 84deebc commit 3d34605
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 4 deletions.
27 changes: 24 additions & 3 deletions src/main/java/net/snowflake/client/core/SessionUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import net.snowflake.client.log.ArgSupplier;
import net.snowflake.client.log.SFLogger;
import net.snowflake.client.log.SFLoggerFactory;
import net.snowflake.client.util.DecorrelatedJitterBackoff;
import net.snowflake.client.util.SecretDetector;
import net.snowflake.client.util.Stopwatch;
import net.snowflake.common.core.SqlState;
Expand Down Expand Up @@ -69,6 +70,7 @@ public class SessionUtil {
private static final String SF_PATH_TOKEN_REQUEST = "/session/token-request";
public static final String SF_PATH_AUTHENTICATOR_REQUEST = "/session/authenticator-request";
public static final String SF_PATH_CONSOLE_LOGIN_REQUEST = "/console/login";
public static final String OKTA_PATH_AUTH = "/api/v1/authn";

public static final String SF_QUERY_SESSION_DELETE = "delete";

Expand Down Expand Up @@ -1456,22 +1458,41 @@ private static void handleFederatedFlowError(SFLoginInput loginInput, Exception
*/
private static String getSamlResponseUsingOkta(SFLoginInput loginInput)
throws SnowflakeSQLException {
DecorrelatedJitterBackoff backoff = new DecorrelatedJitterBackoff(2000, 16000);
long i = 0;
int retryCount = 1;
long backoffMilliSeconds = 0;
String tokenUrl="";
String ssoUrl="";
while (true) {
try {
JsonNode dataNode = federatedFlowStep1(loginInput);
String tokenUrl = dataNode.path("tokenUrl").asText();
String ssoUrl = dataNode.path("ssoUrl").asText();
Thread.sleep(backoffMilliSeconds);
// We don't need to keep sending HTTP requests to the authenticator-request endpoint on every retry
// because it's unlikely that the token and SSO URLs are going to change.
if (retryCount == 1) {
JsonNode dataNode = federatedFlowStep1(loginInput);
tokenUrl = dataNode.path("tokenUrl").asText();
ssoUrl = dataNode.path("ssoUrl").asText();
}
federatedFlowStep2(loginInput, tokenUrl, ssoUrl);
final String oneTimeToken = federatedFlowStep3(loginInput, tokenUrl);
return federatedFlowStep4(loginInput, ssoUrl, oneTimeToken);
} catch (SnowflakeSQLException ex) {
i += 1000; // increment by 1000 ms
// This error gets thrown if the okta request encountered a retry-able error that
// requires getting a new one-time token.
if (ex.getErrorCode() == ErrorCode.AUTHENTICATOR_REQUEST_TIMEOUT.getMessageCode()) {
logger.debug("Failed to get Okta SAML response. Retrying without changing retry count.");
backoffMilliSeconds = backoff.nextSleepTime(i);
logger.debug(
"Going to backoff for " + backoffMilliSeconds + " ms. Retry Count: " + retryCount);
retryCount++;
} else {
throw ex;
}
} catch (InterruptedException ex) {
throw new SnowflakeSQLException(
"Thread was interrupted before retrying Okta request for SAML response.", null);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/net/snowflake/client/jdbc/RestRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,9 @@ public static CloseableHttpResponse execute(

// If this was a request for an Okta one-time token that failed with a retry-able error,
// throw exception to renew the token before trying again.
if (String.valueOf(httpRequest.getURI()).contains("okta.com/api/v1/authn")) {
// TODO: The URI won't necessrily contain okta.com with customers using vanity URLs, i.e
// somecompany.com/api/v1/authn
if (String.valueOf(httpRequest.getURI()).contains("/api/v1/authn")) {
throw new SnowflakeSQLException(
ErrorCode.AUTHENTICATOR_REQUEST_TIMEOUT,
retryCount,
Expand Down

0 comments on commit 3d34605

Please sign in to comment.