static Long expiresAtMillis(String token) {
    if (null == token) {
      return null;
    }

    List<String> parts = Splitter.on('.').splitToList(token);
    if (parts.size() != 3) {
      return null;
    }

    JsonNode node;
    try {
      node = JsonUtil.mapper().readTree(Base64.getUrlDecoder().decode(parts.get(1)));
    } catch (IOException e) {
      return null;
    }

    Long expiresAtSeconds = JsonUtil.getLongOrNull("exp", node);
    if (expiresAtSeconds != null) {
      return TimeUnit.SECONDS.toMillis(expiresAtSeconds);
    }

    return null;
  }
@Test
  public void testExpiresAt() {
    assertThat(OAuth2Util.expiresAtMillis(null)).isNull();
    assertThat(OAuth2Util.expiresAtMillis("not a token")).isNull();

    // expires at epoch second = 1
    String token =
        "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjF9.gQADTbdEv-rpDWKSkGLbmafyB5UUjTdm9B_1izpuZ6E";
    assertThat(OAuth2Util.expiresAtMillis(token)).isEqualTo(1000L);

    // expires at epoch second = 19999999999
    token =
        "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE5OTk5OTk5OTk5fQ._3k92KJi2NTyTG6V1s2mzJ__GiQtL36DnzsZSkBdYPw";
    assertThat(OAuth2Util.expiresAtMillis(token)).isEqualTo(19999999999000L);

    // no expiration
    token =
        "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
    assertThat(OAuth2Util.expiresAtMillis(token)).isNull();
  }