From 09551aabb8756532d3ec5d16855f47266079cbd5 Mon Sep 17 00:00:00 2001
From: sbansla <sbansla@twilio.com>
Date: Tue, 26 Mar 2024 18:05:45 +0530
Subject: [PATCH 1/3] feat: added support for fetching access token from oauth
 server

---
 src/main/java/com/twilio/TwilioOauth.java     |  77 +++++++++
 .../com/twilio/http/BearerTokenRequest.java   |  48 ++++++
 .../java/com/twilio/http/OAuthHttpClient.java | 113 ++++++++++++
 .../java/com/twilio/http/OAuthRequest.java    |  49 ++++++
 src/main/java/com/twilio/http/OAuthTest.java  |  20 +++
 .../com/twilio/http/OAuthTokenManager.java    |  89 ++++++++++
 src/main/java/com/twilio/http/Request.java    |  22 +--
 .../twilio/http/TwilioOAuthRestClient.java    | 162 ++++++++++++++++++
 .../com/twilio/models/OAuthRequestBody.java   |  38 ++++
 .../com/twilio/models/OAuthResponseBody.java  |  56 ++++++
 10 files changed, 663 insertions(+), 11 deletions(-)
 create mode 100644 src/main/java/com/twilio/TwilioOauth.java
 create mode 100644 src/main/java/com/twilio/http/BearerTokenRequest.java
 create mode 100644 src/main/java/com/twilio/http/OAuthHttpClient.java
 create mode 100644 src/main/java/com/twilio/http/OAuthRequest.java
 create mode 100644 src/main/java/com/twilio/http/OAuthTest.java
 create mode 100644 src/main/java/com/twilio/http/OAuthTokenManager.java
 create mode 100644 src/main/java/com/twilio/http/TwilioOAuthRestClient.java
 create mode 100644 src/main/java/com/twilio/models/OAuthRequestBody.java
 create mode 100644 src/main/java/com/twilio/models/OAuthResponseBody.java

diff --git a/src/main/java/com/twilio/TwilioOauth.java b/src/main/java/com/twilio/TwilioOauth.java
new file mode 100644
index 0000000000..b6cf2fb446
--- /dev/null
+++ b/src/main/java/com/twilio/TwilioOauth.java
@@ -0,0 +1,77 @@
+package com.twilio;
+
+import com.twilio.exception.AuthenticationException;
+import com.twilio.http.TwilioOAuthRestClient;
+import lombok.Getter;
+
+import java.util.List;
+
+public class TwilioOauth {
+    public static final String JAVA_VERSION = System.getProperty("java.version");
+    public static final String OS_NAME = System.getProperty("os.name");
+    public static final String OS_ARCH = System.getProperty("os.arch");
+    private static String clientId = System.getenv("TWILIO_CLIENT_ID");
+    private static String clientSecret = System.getenv("TWILIO_CLIENT_SECRET");
+    @Getter
+    private static List<String> userAgentExtensions;
+    private static String region = System.getenv("TWILIO_REGION");
+    private static String edge = System.getenv("TWILIO_EDGE");
+
+    private static volatile TwilioOAuthRestClient oAuthRestClient;
+    
+    private TwilioOauth() {
+    }
+    public static synchronized void init(final String clientId, final String clientSecret) {
+        TwilioOauth.setClientId(clientId);
+        TwilioOauth.setClientSecret(clientSecret);
+    }
+
+    public static synchronized void setClientId(final String clientId) {
+        if (clientId == null) {
+            throw new AuthenticationException("Client Id can not be null");
+        }
+
+        TwilioOauth.clientId = clientId;
+    }
+
+    public static synchronized void setClientSecret(final String clientSecret) {
+        if (clientSecret == null) {
+            throw new AuthenticationException("Client Secret can not be null");
+        }
+
+        TwilioOauth.clientSecret = clientSecret;
+    }
+
+    public static TwilioOAuthRestClient getRestClient() {
+        if (TwilioOauth.oAuthRestClient == null) {
+            synchronized (TwilioOauth.class) {
+                if (TwilioOauth.oAuthRestClient == null) {
+                    TwilioOauth.oAuthRestClient = buildOAuthRestClient();
+                }
+            }
+        }
+
+        return TwilioOauth.oAuthRestClient;
+    }
+
+    private static TwilioOAuthRestClient buildOAuthRestClient() {
+        if (TwilioOauth.clientId == null || TwilioOauth.clientSecret == null) {
+            throw new AuthenticationException(
+                    "TwilioOAuthRestClient was used before ClientId and ClientSecret were set, please call TwilioOauth.init()"
+            );
+        }
+
+        TwilioOAuthRestClient.Builder builder = new TwilioOAuthRestClient.Builder(TwilioOauth.clientId, TwilioOauth.clientSecret);
+
+        if (userAgentExtensions != null) {
+            builder.userAgentExtensions(TwilioOauth.userAgentExtensions);
+        }
+
+        builder.region(TwilioOauth.region);
+        builder.edge(TwilioOauth.edge);
+
+        return builder.build();
+    }
+
+
+}
diff --git a/src/main/java/com/twilio/http/BearerTokenRequest.java b/src/main/java/com/twilio/http/BearerTokenRequest.java
new file mode 100644
index 0000000000..5302975af8
--- /dev/null
+++ b/src/main/java/com/twilio/http/BearerTokenRequest.java
@@ -0,0 +1,48 @@
+package com.twilio.http;
+
+public class BearerTokenRequest extends Request {
+
+    protected static final String DEFAULT_REGION = "us1";
+    public static final String QUERY_STRING_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
+    public static final String QUERY_STRING_DATE_FORMAT = "yyyy-MM-dd";
+
+    private String bearerToken;
+
+    /**
+     * Create a new API request.
+     *
+     * @param method HTTP method
+     * @param url    url of request
+     */
+    public BearerTokenRequest(final HttpMethod method, final String url) {
+        super(method, url);
+    }
+
+    /**
+     * Create a new API request.
+     *
+     * @param method HTTP method
+     * @param domain Twilio domain
+     * @param uri    uri of request
+     */
+    public BearerTokenRequest(final HttpMethod method, final String domain, final String uri) {
+        this(method, domain, uri, null);
+    }
+
+    /**
+     * Create a new API request.
+     *
+     * @param method HTTP Method
+     * @param domain Twilio domain
+     * @param uri    uri of request
+     * @param region region to make request
+     */
+    public BearerTokenRequest(
+            final HttpMethod method,
+            final String domain,
+            final String uri,
+            final String region
+    ) {
+        super(method, domain, uri, region);
+    }
+}
diff --git a/src/main/java/com/twilio/http/OAuthHttpClient.java b/src/main/java/com/twilio/http/OAuthHttpClient.java
new file mode 100644
index 0000000000..a6e1426dc1
--- /dev/null
+++ b/src/main/java/com/twilio/http/OAuthHttpClient.java
@@ -0,0 +1,113 @@
+package com.twilio.http;
+
+import com.twilio.Twilio;
+import com.twilio.constant.EnumConstants;
+import com.twilio.exception.ApiException;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.client.utils.HttpClientUtils;
+import org.apache.http.config.SocketConfig;
+import org.apache.http.entity.BufferedHttpEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.message.BasicHeader;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+public class OAuthHttpClient extends HttpClient {
+    protected final org.apache.http.client.HttpClient client;
+
+    public OAuthHttpClient() {
+        this(DEFAULT_REQUEST_CONFIG);
+    }
+
+    public OAuthHttpClient(final RequestConfig requestConfig) {
+        this(requestConfig, DEFAULT_SOCKET_CONFIG);
+    }
+
+    public OAuthHttpClient(final RequestConfig requestConfig, final SocketConfig socketConfig) {
+        Collection<BasicHeader> headers = Arrays.asList(
+                new BasicHeader("X-Twilio-Client", "java-" + Twilio.VERSION),
+                new BasicHeader(HttpHeaders.ACCEPT, "application/json"),
+                new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "utf-8")
+        );
+
+        String googleAppEngineVersion = System.getProperty("com.google.appengine.runtime.version");
+        boolean isGoogleAppEngine = googleAppEngineVersion != null && !googleAppEngineVersion.isEmpty();
+
+        org.apache.http.impl.client.HttpClientBuilder clientBuilder = HttpClientBuilder.create();
+
+        if (!isGoogleAppEngine) {
+            clientBuilder.useSystemProperties();
+        }
+
+        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
+        connectionManager.setDefaultSocketConfig(socketConfig);
+        /*
+         *  Example: Lets say client has one server.
+         *  There are 4 servers on edge handling client request.
+         *  Each request takes on an average 500ms (2 request per second)
+         *  Total number request can be server in a second from a route: 20 * 4 * 2 (DefaultMaxPerRoute * edge servers * request per second)
+         */
+        connectionManager.setDefaultMaxPerRoute(5);
+        connectionManager.setMaxTotal(20);
+
+        client = clientBuilder
+                .setConnectionManager(connectionManager)
+                .setDefaultRequestConfig(requestConfig)
+                .setDefaultHeaders(headers)
+                .setRedirectStrategy(this.getRedirectStrategy())
+                .build();
+    }
+    
+    @Override
+    public Response makeRequest(Request request) {
+        HttpMethod method = request.getMethod();
+        RequestBuilder builder = RequestBuilder.create(method.toString())
+                .setUri(request.constructURL().toString())
+                .setVersion(HttpVersion.HTTP_1_1)
+                .setCharset(StandardCharsets.UTF_8);
+        
+
+        for (Map.Entry<String, List<String>> entry : request.getHeaderParams().entrySet()) {
+            for (String value : entry.getValue()) {
+                builder.addHeader(entry.getKey(), value);
+            }
+        }
+        HttpEntity requestEntity = new StringEntity(request.getBody(), ContentType.APPLICATION_JSON);
+        builder.setEntity(requestEntity);
+        builder.addHeader(
+                HttpHeaders.CONTENT_TYPE, EnumConstants.ContentType.JSON.getValue());
+        builder.addHeader(HttpHeaders.USER_AGENT, HttpUtility.getUserAgentString(request.getUserAgentExtensions()));
+        HttpResponse response = null;
+
+        try {
+            response = client.execute(builder.build());
+            HttpEntity entity = response.getEntity();
+            return new Response(
+                    // Consume the entire HTTP response before returning the stream
+                    entity == null ? null : new BufferedHttpEntity(entity).getContent(),
+                    response.getStatusLine().getStatusCode(),
+                    response.getAllHeaders()
+            );
+        } catch (IOException e) {
+            throw new ApiException(e.getMessage(), e);
+        } finally {
+
+            // Ensure this response is properly closed
+            HttpClientUtils.closeQuietly(response);
+
+        }
+    }
+}
diff --git a/src/main/java/com/twilio/http/OAuthRequest.java b/src/main/java/com/twilio/http/OAuthRequest.java
new file mode 100644
index 0000000000..f17f231221
--- /dev/null
+++ b/src/main/java/com/twilio/http/OAuthRequest.java
@@ -0,0 +1,49 @@
+package com.twilio.http;
+
+public class OAuthRequest extends Request {
+
+    protected static final String DEFAULT_REGION = "us1";
+    public static final String QUERY_STRING_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
+    public static final String QUERY_STRING_DATE_FORMAT = "yyyy-MM-dd";
+
+    private String clientId;
+    private String clientSecret;
+
+    /**
+     * Create a new API request.
+     *
+     * @param method HTTP method
+     * @param url    url of request
+     */
+    public OAuthRequest(final HttpMethod method, final String url) {
+        super(method, url);
+    }
+
+    /**
+     * Create a new API request.
+     *
+     * @param method HTTP method
+     * @param domain Twilio domain
+     * @param uri    uri of request
+     */
+    public OAuthRequest(final HttpMethod method, final String domain, final String uri) {
+        this(method, domain, uri, null);
+    }
+
+    /**
+     * Create a new API request.
+     *
+     * @param method HTTP Method
+     * @param domain Twilio domain
+     * @param uri    uri of request
+     * @param region region to make request
+     */
+    public OAuthRequest(
+            final HttpMethod method,
+            final String domain,
+            final String uri,
+            final String region
+    ) {
+        super(method, domain, uri, region);
+    }
+}
diff --git a/src/main/java/com/twilio/http/OAuthTest.java b/src/main/java/com/twilio/http/OAuthTest.java
new file mode 100644
index 0000000000..91c45e7b7f
--- /dev/null
+++ b/src/main/java/com/twilio/http/OAuthTest.java
@@ -0,0 +1,20 @@
+package com.twilio.http;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.twilio.Twilio;
+import com.twilio.rest.Domains;
+import com.twilio.rest.content.v1.Content;
+
+public class OAuthTest {
+    public static void main(String[] args) {
+        String clientId = "zMqwdhvVP8VUkzkPrxKl0vRnmugVJXar";
+        String clientSecret = "9O-04YgPwbliyG2KEDE8yWH1m0WMNY2Qz_tjWTQ0mRSflzezUDumMuyAAeZFsjDF";
+        //Twilio.init(clientId, clientSecret);
+        String path = "https://api.twilio-dev.auth0app.com/oauth/token";
+        Content.ContentCreateRequest contentCreateRequest = new Content.ContentCreateRequest(
+                "client_credentials", "https://www.twilio.com/organizations", clientId, clientSecret);
+        Content content = Content.creator(contentCreateRequest).create();
+        System.out.println(content);
+       
+    }
+}
diff --git a/src/main/java/com/twilio/http/OAuthTokenManager.java b/src/main/java/com/twilio/http/OAuthTokenManager.java
new file mode 100644
index 0000000000..f26c698dbd
--- /dev/null
+++ b/src/main/java/com/twilio/http/OAuthTokenManager.java
@@ -0,0 +1,89 @@
+package com.twilio.http;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.twilio.TwilioOauth;
+import com.twilio.exception.ApiConnectionException;
+import com.twilio.exception.ApiException;
+import com.twilio.exception.RestException;
+import com.twilio.models.OAuthRequestBody;
+import com.twilio.models.OAuthResponseBody;
+import com.twilio.rest.content.v1.Content;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class OAuthTokenManager {
+    private static OAuthTokenManager instance;
+    private OAuthRequestBody oAuthRequestBody;
+    private OAuthResponseBody oAuthResponseBody;
+    
+    private TwilioOAuthRestClient twilioOAuthRestClient = TwilioOauth.getRestClient();
+
+    public static OAuthTokenManager getInstance() {
+        if (instance == null) {
+            synchronized (OAuthTokenManager.class) {
+                if (instance == null) {
+                    instance = new OAuthTokenManager();
+                }
+            }
+        }
+        return instance;
+    }
+    
+    private OAuthTokenManager() { }
+    
+    public String fetchToken(OAuthRequest request) {
+        if (oAuthResponseBody != null && !oAuthResponseBody.isAccessTokenExpired()) {
+            System.out.println("Using existing token");
+            return oAuthResponseBody.getAccessToken();
+        }
+        this.oAuthResponseBody = null;
+        System.out.println("Fetching token from server");
+        Response response = twilioOAuthRestClient.request(request);
+        handleResponseExceptions(response);
+        
+        this.oAuthResponseBody = fromJson(response.getStream(), twilioOAuthRestClient.getObjectMapper());
+        if (oAuthResponseBody.getAccessToken() == null || oAuthResponseBody.getAccessToken().isEmpty()) {
+            throw new ApiException(
+                    "OAuth access token is missing or invalid."
+            );
+        }
+        return this.oAuthResponseBody.getAccessToken();
+    }
+
+    private void handleResponseExceptions(Response response) {
+        if (response == null) {
+            throw new ApiConnectionException(
+                    "Content creation failed: Unable to connect to server"
+            );
+        } else if (!TwilioOAuthRestClient.SUCCESS.test(response.getStatusCode())) {
+            RestException restException = RestException.fromJson(
+                    response.getStream(),
+                    twilioOAuthRestClient.getObjectMapper()
+            );
+            if (restException == null) {
+                throw new ApiException(
+                        "Server Error, no content",
+                        response.getStatusCode()
+                );
+            }
+            throw new ApiException(restException);
+        }
+    }
+
+    private OAuthResponseBody fromJson(
+            final InputStream json,
+            final ObjectMapper objectMapper
+    ) {
+        // Convert all checked exceptions to Runtime
+        try {
+            return objectMapper.readValue(json, OAuthResponseBody.class);
+        } catch (final JsonMappingException | JsonParseException e) {
+            throw new ApiException(e.getMessage(), e);
+        } catch (final IOException e) {
+            throw new ApiConnectionException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/src/main/java/com/twilio/http/Request.java b/src/main/java/com/twilio/http/Request.java
index 36aa7a5e03..39caabfa02 100644
--- a/src/main/java/com/twilio/http/Request.java
+++ b/src/main/java/com/twilio/http/Request.java
@@ -25,22 +25,22 @@ public class Request {
     public static final String QUERY_STRING_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
     public static final String QUERY_STRING_DATE_FORMAT = "yyyy-MM-dd";
 
-    private final HttpMethod method;
-    private final String url;
-    private final Map<String, List<String>> queryParams;
-    private final Map<String, List<String>> postParams;
-    private final Map<String, List<String>> headerParams;
-
-    private String region;
-    private String edge;
+    protected final HttpMethod method;
+    protected final String url;
+    protected final Map<String, List<String>> queryParams;
+    protected final Map<String, List<String>> postParams;
+    protected final Map<String, List<String>> headerParams;
+
+    protected String region;
+    protected String edge;
     private String username;
     private String password;
 
-    private List<String> userAgentExtensions;
+    protected List<String> userAgentExtensions;
 
-    private EnumConstants.ContentType contentType;
+    protected EnumConstants.ContentType contentType;
 
-    private String body;
+    protected String body;
 
     /**
      * Create a new API request.
diff --git a/src/main/java/com/twilio/http/TwilioOAuthRestClient.java b/src/main/java/com/twilio/http/TwilioOAuthRestClient.java
new file mode 100644
index 0000000000..6c388fd5bd
--- /dev/null
+++ b/src/main/java/com/twilio/http/TwilioOAuthRestClient.java
@@ -0,0 +1,162 @@
+package com.twilio.http;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.twilio.exception.ApiConnectionException;
+import com.twilio.exception.ApiException;
+import com.twilio.exception.RestException;
+import com.twilio.models.OAuthRequestBody;
+import com.twilio.rest.content.v1.Content;
+import lombok.Getter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+public class TwilioOAuthRestClient {
+    public static final int HTTP_STATUS_CODE_CREATED = 201;
+    public static final int HTTP_STATUS_CODE_NO_CONTENT = 204;
+    public static final int HTTP_STATUS_CODE_OK = 200;
+    public static final Predicate<Integer> SUCCESS = i -> i != null && i >= 200 && i < 400;
+    @Getter
+    private final ObjectMapper objectMapper;
+    @Getter
+    private final String region;
+    @Getter
+    private final String edge;
+    
+    private OAuthRequestBody oAuthRequestBody;
+    
+    private static final Logger logger = LoggerFactory.getLogger(TwilioOAuthRestClient.class);
+    
+    @Getter
+    private final HttpClient httpClient;
+    @Getter
+    private final List<String> userAgentExtensions;
+    protected TwilioOAuthRestClient(Builder builder) {
+        this.oAuthRequestBody = builder.oAuthModel;
+        this.region = builder.region;
+        this.edge = builder.edge;
+        this.httpClient = builder.httpClient;
+        this.objectMapper = new ObjectMapper();
+        this.userAgentExtensions = builder.userAgentExtensions;
+    }
+    public static class Builder {
+        private OAuthRequestBody oAuthModel;
+        private String region;
+        private String edge;
+        private HttpClient httpClient;
+        private List<String> userAgentExtensions;
+
+        public Builder(final String clientId, final String clientSecret) {
+            this.oAuthModel = new OAuthRequestBody(clientId, clientSecret);
+        }
+
+        public TwilioOAuthRestClient.Builder region(final String region) {
+            this.region = region;
+            return this;
+        }
+
+        public TwilioOAuthRestClient.Builder edge(final String edge) {
+            this.edge = edge;
+            return this;
+        }
+
+        public TwilioOAuthRestClient.Builder httpClient(final HttpClient httpClient) {
+            this.httpClient = httpClient;
+            return this;
+        }
+
+        public TwilioOAuthRestClient.Builder userAgentExtensions(final List<String> userAgentExtensions) {
+            if (userAgentExtensions != null && !userAgentExtensions.isEmpty()) {
+                this.userAgentExtensions = new ArrayList<>(userAgentExtensions);
+            }
+            return this;
+        }
+        
+        public TwilioOAuthRestClient build() {
+            if (this.httpClient == null) {
+                this.httpClient = new OAuthHttpClient();
+            }
+            return new TwilioOAuthRestClient(this);
+        }
+    }
+
+    public Response request(final OAuthRequest request) {
+
+        buildRequest(request);
+        logRequest(request);
+        Response response = httpClient.reliableRequest(request);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("status code: {}", response.getStatusCode());
+            org.apache.http.Header[] responseHeaders = response.getHeaders();
+            logger.debug("response headers:");
+            for (int i = 0; i < responseHeaders.length; i++) {
+                logger.debug("responseHeader: {}", responseHeaders[i]);
+            }
+        }
+        return response;
+    }
+    
+    private void buildRequest(OAuthRequest request) {
+        request.setBody(Content.toJson(oAuthRequestBody, objectMapper));
+        if (region != null)
+            request.setRegion(region);
+        if (edge != null)
+            request.setEdge(edge);
+
+        if (userAgentExtensions != null && !userAgentExtensions.isEmpty()) {
+            request.setUserAgentExtensions(userAgentExtensions);
+        }
+    }
+    
+    private void handleOAuthResponse(Response response) {
+        if (response == null) {
+            throw new ApiConnectionException(
+                    "Content creation failed: Unable to connect to server"
+            );
+        } else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) {
+            RestException restException = RestException.fromJson(
+                    response.getStream(),
+                    objectMapper
+            );
+            if (restException == null) {
+                throw new ApiException(
+                        "Server Error, no content",
+                        response.getStatusCode()
+                );
+            }
+            throw new ApiException(restException);
+        }
+    }
+
+
+
+    public void logRequest(final Request request) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("-- BEGIN Twilio API Request --");
+            logger.debug("request method: " + request.getMethod());
+            logger.debug("request URL: " + request.constructURL().toString());
+            final Map<String, List<String>> queryParams = request.getQueryParams();
+            final Map<String, List<String>> headerParams = request.getHeaderParams();
+
+            if (queryParams != null && !queryParams.isEmpty()) {
+                logger.debug("query parameters: " + queryParams);
+            }
+
+            if (headerParams != null && !headerParams.isEmpty()) {
+                logger.debug("header parameters: ");
+                for (String key : headerParams.keySet()) {
+                    if (!key.toLowerCase().contains("authorization")) {
+                        logger.debug(key + ": " + headerParams.get(key));
+                    }
+                }
+            }
+
+            logger.debug("-- END Twilio API Request --");
+        }
+    }
+}
diff --git a/src/main/java/com/twilio/models/OAuthRequestBody.java b/src/main/java/com/twilio/models/OAuthRequestBody.java
new file mode 100644
index 0000000000..8034262a5c
--- /dev/null
+++ b/src/main/java/com/twilio/models/OAuthRequestBody.java
@@ -0,0 +1,38 @@
+package com.twilio.models;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@ToString
+public class OAuthRequestBody {
+
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonProperty("client_id")
+    @Setter
+    private String clientId;
+
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonProperty("client_secret")
+    @Setter
+    private String clientSecret;
+
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonProperty("audience")
+    @Getter
+    @Setter
+    private String audience = "https://www.twilio.com/organizations";
+
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonProperty("grant_type")
+    @Getter
+    @Setter
+    private String grantType = "client_credentials";
+    
+    public OAuthRequestBody(String clientId, String clientSecret) {
+        this.clientId = clientId;
+        this.clientSecret = clientSecret;
+    }
+}
diff --git a/src/main/java/com/twilio/models/OAuthResponseBody.java b/src/main/java/com/twilio/models/OAuthResponseBody.java
new file mode 100644
index 0000000000..73064bbadc
--- /dev/null
+++ b/src/main/java/com/twilio/models/OAuthResponseBody.java
@@ -0,0 +1,56 @@
+package com.twilio.models;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSObject;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.MACVerifier;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jwt.SignedJWT;
+
+
+import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Optional;
+
+@ToString
+public class OAuthResponseBody {
+
+    @JsonProperty("access_token")
+    @Getter
+    @Setter
+    private String accessToken;
+
+    @JsonProperty("expires_in")
+    @Getter
+    @Setter
+    private String expiresIn;
+
+    @JsonProperty("token_type")
+    @Getter
+    @Setter
+    private String tokenType;
+
+    public boolean isAccessTokenExpired() {
+        DecodedJWT jwt = JWT.decode(this.accessToken);
+        Date expiresAt = jwt.getExpiresAt();
+        // Add a buffer of 30 seconds
+        long bufferMilliseconds = 30 * 1000;
+        Date bufferExpiresAt = new Date(expiresAt.getTime() - bufferMilliseconds);
+        return bufferExpiresAt.before(new Date());
+    }
+}
+
+/*
+
+
+eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Inc3ODVXNHF0dTVVS21FcXR3dEhqTSJ9.eyJodHRwOi8vdHdpbGlvL3ZhbGlkcmVnaW9ucyI6ImRldi11czEiLCJodHRwOi8vdHdpbGlvL2FjdCI6eyJzdWIiOiJPUjg2N2I1NTQ3MGY0MTJjODYyOGVmMDBhODIxODQzZTExIn0sImh0dHA6Ly90d2lsaW8vc3ViIjoiT1I4NjdiNTU0NzBmNDEyYzg2MjhlZjAwYTgyMTg0M2UxMSIsImh0dHA6Ly90d2lsaW8vdHlwIjoidm5kLnR3aWxpby5vYXV0aC5hdCtqd3Q7IiwiaXNzIjoiaHR0cHM6Ly9hcGkudHdpbGlvLWRldi5hdXRoMGFwcC5jb20vIiwic3ViIjoiek1xd2RodlZQOFZVa3prUHJ4S2wwdlJubXVnVkpYYXJAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vd3d3LnR3aWxpby5jb20vb3JnYW5pemF0aW9ucyIsImlhdCI6MTcxMTQ0MjI5MiwiZXhwIjoxNzExNTI4NjkyLCJhenAiOiJ6TXF3ZGh2VlA4VlVremtQcnhLbDB2Um5tdWdWSlhhciIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.TJhBH57_KWWIyrvXw-c-dAzs9NlcfKz2uhc1EobO58ruI3njm3A_3hEBiIyqS3LT7_klz-wDqK2fP-pP8UJoA_fYLtD7iu93Q60PyKkJw-IGknkhIqL8mOpq0C5ViWKrieT9BCjHda6fN5o9zRmS0tSZ8Mhey6t3V5JUuTOs4RR_goU22TXELimlSx7LA5jBmjtt43kNDuewrOO_jrGGrZZKTNsWJo-ZYQ6dFr4BKEYYp3YzNWY-dG7CFc7QRY7BHxbkSjHNv1LrJ35aOMiLXr8XEDU1FMilErLSDtf0I9Qcb9ErsM2xMOVqbAu9JVp5W3svVGVkfjfj3cTILqDiAQ
+
+ */

From 18c8fd5662d08d4d49389e2f80f6fa1a2e07e41e Mon Sep 17 00:00:00 2001
From: sbansla <sbansla@twilio.com>
Date: Tue, 26 Mar 2024 18:10:59 +0530
Subject: [PATCH 2/3] chore: imported oauth libs

---
 pom.xml                                       |  6 +++++
 src/main/java/com/twilio/http/OAuthTest.java  | 20 ----------------
 .../com/twilio/models/OAuthResponseBody.java  | 24 +++----------------
 3 files changed, 9 insertions(+), 41 deletions(-)
 delete mode 100644 src/main/java/com/twilio/http/OAuthTest.java

diff --git a/pom.xml b/pom.xml
index 2e86d53bf6..be2e732573 100644
--- a/pom.xml
+++ b/pom.xml
@@ -315,6 +315,12 @@
       <version>1.10.19</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.auth0</groupId>
+      <artifactId>java-jwt</artifactId>
+      <version>4.4.0</version>
+    </dependency>
+
   </dependencies>
   <build>
     <plugins>
diff --git a/src/main/java/com/twilio/http/OAuthTest.java b/src/main/java/com/twilio/http/OAuthTest.java
deleted file mode 100644
index 91c45e7b7f..0000000000
--- a/src/main/java/com/twilio/http/OAuthTest.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.twilio.http;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.twilio.Twilio;
-import com.twilio.rest.Domains;
-import com.twilio.rest.content.v1.Content;
-
-public class OAuthTest {
-    public static void main(String[] args) {
-        String clientId = "zMqwdhvVP8VUkzkPrxKl0vRnmugVJXar";
-        String clientSecret = "9O-04YgPwbliyG2KEDE8yWH1m0WMNY2Qz_tjWTQ0mRSflzezUDumMuyAAeZFsjDF";
-        //Twilio.init(clientId, clientSecret);
-        String path = "https://api.twilio-dev.auth0app.com/oauth/token";
-        Content.ContentCreateRequest contentCreateRequest = new Content.ContentCreateRequest(
-                "client_credentials", "https://www.twilio.com/organizations", clientId, clientSecret);
-        Content content = Content.creator(contentCreateRequest).create();
-        System.out.println(content);
-       
-    }
-}
diff --git a/src/main/java/com/twilio/models/OAuthResponseBody.java b/src/main/java/com/twilio/models/OAuthResponseBody.java
index 73064bbadc..c07e0f280e 100644
--- a/src/main/java/com/twilio/models/OAuthResponseBody.java
+++ b/src/main/java/com/twilio/models/OAuthResponseBody.java
@@ -1,24 +1,13 @@
 package com.twilio.models;
 
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.interfaces.DecodedJWT;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.exceptions.JWTDecodeException;
-import com.auth0.jwt.interfaces.DecodedJWT;
-import com.nimbusds.jose.JOSEException;
-import com.nimbusds.jose.JWSObject;
-import com.nimbusds.jose.JWSVerifier;
-import com.nimbusds.jose.crypto.MACVerifier;
-import com.nimbusds.jose.crypto.RSASSAVerifier;
-import com.nimbusds.jwt.SignedJWT;
-
 
-import java.security.interfaces.RSAPublicKey;
-import java.text.ParseException;
 import java.util.Date;
-import java.util.Optional;
 
 @ToString
 public class OAuthResponseBody {
@@ -46,11 +35,4 @@ public boolean isAccessTokenExpired() {
         Date bufferExpiresAt = new Date(expiresAt.getTime() - bufferMilliseconds);
         return bufferExpiresAt.before(new Date());
     }
-}
-
-/*
-
-
-eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Inc3ODVXNHF0dTVVS21FcXR3dEhqTSJ9.eyJodHRwOi8vdHdpbGlvL3ZhbGlkcmVnaW9ucyI6ImRldi11czEiLCJodHRwOi8vdHdpbGlvL2FjdCI6eyJzdWIiOiJPUjg2N2I1NTQ3MGY0MTJjODYyOGVmMDBhODIxODQzZTExIn0sImh0dHA6Ly90d2lsaW8vc3ViIjoiT1I4NjdiNTU0NzBmNDEyYzg2MjhlZjAwYTgyMTg0M2UxMSIsImh0dHA6Ly90d2lsaW8vdHlwIjoidm5kLnR3aWxpby5vYXV0aC5hdCtqd3Q7IiwiaXNzIjoiaHR0cHM6Ly9hcGkudHdpbGlvLWRldi5hdXRoMGFwcC5jb20vIiwic3ViIjoiek1xd2RodlZQOFZVa3prUHJ4S2wwdlJubXVnVkpYYXJAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vd3d3LnR3aWxpby5jb20vb3JnYW5pemF0aW9ucyIsImlhdCI6MTcxMTQ0MjI5MiwiZXhwIjoxNzExNTI4NjkyLCJhenAiOiJ6TXF3ZGh2VlA4VlVremtQcnhLbDB2Um5tdWdWSlhhciIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.TJhBH57_KWWIyrvXw-c-dAzs9NlcfKz2uhc1EobO58ruI3njm3A_3hEBiIyqS3LT7_klz-wDqK2fP-pP8UJoA_fYLtD7iu93Q60PyKkJw-IGknkhIqL8mOpq0C5ViWKrieT9BCjHda6fN5o9zRmS0tSZ8Mhey6t3V5JUuTOs4RR_goU22TXELimlSx7LA5jBmjtt43kNDuewrOO_jrGGrZZKTNsWJo-ZYQ6dFr4BKEYYp3YzNWY-dG7CFc7QRY7BHxbkSjHNv1LrJ35aOMiLXr8XEDU1FMilErLSDtf0I9Qcb9ErsM2xMOVqbAu9JVp5W3svVGVkfjfj3cTILqDiAQ
-
- */
+}
\ No newline at end of file

From 05f1249cccd706298d53c63af28334b2b8165500 Mon Sep 17 00:00:00 2001
From: sbansla <sbansla@twilio.com>
Date: Tue, 26 Mar 2024 18:29:47 +0530
Subject: [PATCH 3/3] chore: ignored jackson dependency in auth0

---
 pom.xml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/pom.xml b/pom.xml
index be2e732573..0fcd999259 100644
--- a/pom.xml
+++ b/pom.xml
@@ -319,6 +319,12 @@
       <groupId>com.auth0</groupId>
       <artifactId>java-jwt</artifactId>
       <version>4.4.0</version>
+      <exclusions>
+        <exclusion>
+          <groupId>com.fasterxml.jackson.core</groupId>
+          <artifactId>jackson-databind</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
 
   </dependencies>