public static OAuthBearerValidationResult validateIssuedAt(OAuthBearerUnsecuredJws jwt, boolean required,
            long whenCheckTimeMs, int allowableClockSkewMs) throws OAuthBearerConfigException {
        Number value;
        try {
            value = Objects.requireNonNull(jwt).issuedAt();
        } catch (OAuthBearerIllegalTokenException e) {
            return e.reason();
        }
        boolean exists = value != null;
        if (!exists)
            return doesNotExistResult(required, "iat");
        double doubleValue = value.doubleValue();
        return 1000 * doubleValue > whenCheckTimeMs + confirmNonNegative(allowableClockSkewMs)
                ? OAuthBearerValidationResult.newFailure(String.format(
                        "The Issued At value (%f seconds) was after the indicated time (%d ms) plus allowable clock skew (%d ms)",
                        doubleValue, whenCheckTimeMs, allowableClockSkewMs))
                : OAuthBearerValidationResult.newSuccess();
    }
@Test
    public void validateIssuedAt() {
        long nowMs = TIME.milliseconds();
        double nowClaimValue = ((double) nowMs) / 1000;
        for (boolean exists : new boolean[] {true, false}) {
            StringBuilder sb = new StringBuilder("{");
            appendJsonText(sb, "exp", nowClaimValue);
            appendCommaJsonText(sb, "sub", "principalName");
            if (exists)
                appendCommaJsonText(sb, "iat", nowClaimValue);
            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 (boolean required : new boolean[] {true, false}) {
                for (int allowableClockSkewMs : new int[] {0, 5, 10, 20}) {
                    for (long whenCheckOffsetMs : new long[] {-10, 0, 10}) {
                        long whenCheckMs = nowMs + whenCheckOffsetMs;
                        OAuthBearerValidationResult result = OAuthBearerValidationUtils.validateIssuedAt(testJwt,
                                required, whenCheckMs, allowableClockSkewMs);
                        if (required && !exists)
                            assertTrue(isFailureWithMessageAndNoFailureScope(result), "useErrorValue || required && !exists");
                        else if (!required && !exists)
                            assertTrue(isSuccess(result), "!required && !exists");
                        else if (nowClaimValue * 1000 > whenCheckMs + allowableClockSkewMs) // issued in future
                            assertTrue(isFailureWithMessageAndNoFailureScope(result),
                                assertionFailureMessage(nowClaimValue, allowableClockSkewMs, whenCheckMs));
                        else
                            assertTrue(isSuccess(result),
                                assertionFailureMessage(nowClaimValue, allowableClockSkewMs, whenCheckMs));
                    }
                }
            }
        }
    }