docs: public void authenticate() {
          URL url;
          try {
              url = new URL(this.getTokenURL());
          } catch (MalformedURLException e) {
              assert false : "An invalid token URL indicates a bug in the SDK.";
              throw new RuntimeException("An invalid token URL indicates a bug in the SDK.", e);
          }
  
          String jwtAssertion = this.constructJWTAssertion();
  
          String urlParameters = String.format(JWT_GRANT_TYPE, this.getClientID(), this.getClientSecret(), jwtAssertion);
  
          BoxAPIRequest request = new BoxAPIRequest(this, url, "POST");
          request.shouldAuthenticate(false);
          request.setBody(urlParameters);
  
          String json;
          try {
              BoxJSONResponse response = (BoxJSONResponse) request.send();
              json = response.getJSON();
          } catch (BoxAPIException ex) {
              // Use the Date advertised by the Box server as the current time to synchronize clocks
              List<String> responseDates = ex.getHeaders().get("Date");
              NumericDate currentTime;
              if (responseDates != null) {
                  String responseDate = responseDates.get(0);
                  SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz");
                  try {
                      Date date = dateFormat.parse(responseDate);
                      currentTime = NumericDate.fromMilliseconds(date.getTime());
                  } catch (ParseException e) {
                      currentTime = NumericDate.now();
                  }
              } else {
                  currentTime = NumericDate.now();
              }
  
              // Reconstruct the JWT assertion, which regenerates the jti claim, with the new "current" time
              jwtAssertion = this.constructJWTAssertion(currentTime);
              urlParameters = String.format(JWT_GRANT_TYPE, this.getClientID(), this.getClientSecret(), jwtAssertion);
  
              // Re-send the updated request
              request = new BoxAPIRequest(this, url, "POST");
              request.shouldAuthenticate(false);
              request.setBody(urlParameters);
  
              BoxJSONResponse response = (BoxJSONResponse) request.send();
              json = response.getJSON();
          }
  
          JsonObject jsonObject = JsonObject.readFrom(json);
          this.setAccessToken(jsonObject.get("access_token").asString());
          this.setLastRefresh(System.currentTimeMillis());
          this.setExpires(jsonObject.get("expires_in").asLong() * 1000);
  
          //if token cache is specified, save to cache
          if (this.accessTokenCache != null) {
              String key = this.getAccessTokenCacheKey();
              JsonObject accessTokenCacheInfo = new JsonObject()
                      .add("accessToken", this.getAccessToken())
                      .add("lastRefresh", this.getLastRefresh())
                      .add("expires", this.getExpires());
  
              this.accessTokenCache.put(key, accessTokenCacheInfo.toString());
          }
      }