private static String composeRawResponse(
            String accessToken, @Nullable String tokenType,
            @Nullable Instant issuedAt, @Nullable Duration expiresIn,
            @Nullable String refreshToken, @Nullable String scope, Map<String, String> extras) {

        final ObjectNode node = JSON.createObjectNode();
        node.put(ACCESS_TOKEN, accessToken);
        if (tokenType != null) {
            node.put(TOKEN_TYPE, tokenType);
        }
        if (issuedAt != null) {
            node.put(ISSUED_AT, ISO_INSTANT.format(issuedAt));
        }
        if (expiresIn != null) {
            node.put(EXPIRES_IN, expiresIn.getSeconds());
        }
        if (refreshToken != null) {
            node.put(REFRESH_TOKEN, refreshToken);
        }
        if (scope != null) {
            node.put(SCOPE, scope);
        }
        extras.forEach(node::put);

        try {
            return JSON.writeValueAsString(node);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
{@code JSON}-formatted raw Token Introspection Response body. If the {@link GrantedOAuth2AccessToken} was
not parsed out of the raw response body, this value calculated based on the other
{@link GrantedOAuth2AccessToken} values.
oauth2/src/main/java/com/linecorp/armeria/common/auth/oauth2/GrantedOAuth2AccessToken.java