public static OAuthBearerValidationResult validateExpirationTime(OAuthBearerUnsecuredJws jwt, long whenCheckTimeMs,
            int allowableClockSkewMs) throws OAuthBearerConfigException {
        Number value;
        try {
            value = Objects.requireNonNull(jwt).expirationTime();
        } catch (OAuthBearerIllegalTokenException e) {
            return e.reason();
        }
        boolean exists = value != null;
        if (!exists)
            return doesNotExistResult(true, "exp");
        double doubleValue = value.doubleValue();
        return whenCheckTimeMs - confirmNonNegative(allowableClockSkewMs) >= 1000 * doubleValue
                ? OAuthBearerValidationResult.newFailure(String.format(
                        "The indicated time (%d ms) minus allowable clock skew (%d ms) was on or after the Expiration Time value (%f seconds)",
                        whenCheckTimeMs, allowableClockSkewMs, doubleValue))
                : OAuthBearerValidationResult.newSuccess();
    }
@Test
    public void validateExpirationTime() {
        long nowMs = TIME.milliseconds();
        double nowClaimValue = ((double) nowMs) / 1000;
        StringBuilder sb = new StringBuilder("{");
        appendJsonText(sb, "exp", nowClaimValue);
        appendCommaJsonText(sb, "sub", "principalName");
        sb.append("}");
        String compactSerialization = HEADER_COMPACT_SERIALIZATION
                + Base64.getUrlEncoder().withoutPadding().encodeToString(sb.toString().getBytes(StandardCharsets.UTF_8))
                + ".";
        OAuthBearerUnsecuredJws testJwt = new OAuthBearerUnsecuredJws(compactSerialization, "sub", "scope");
        for (int allowableClockSkewMs : new int[] {0, 5, 10, 20}) {
            for (long whenCheckOffsetMs : new long[] {-10, 0, 10}) {
                long whenCheckMs = nowMs + whenCheckOffsetMs;
                OAuthBearerValidationResult result = OAuthBearerValidationUtils.validateExpirationTime(testJwt,
                        whenCheckMs, allowableClockSkewMs);
                if (whenCheckMs - allowableClockSkewMs >= nowClaimValue * 1000) // expired
                    assertTrue(isFailureWithMessageAndNoFailureScope(result),
                        assertionFailureMessage(nowClaimValue, allowableClockSkewMs, whenCheckMs));
                else
                    assertTrue(isSuccess(result), assertionFailureMessage(nowClaimValue, allowableClockSkewMs, whenCheckMs));
            }
        }
    }