Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added support for fetching access token from oauth server #786

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,18 @@
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<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>
<build>
<plugins>
Expand Down
77 changes: 77 additions & 0 deletions src/main/java/com/twilio/TwilioOauth.java
Original file line number Diff line number Diff line change
@@ -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();
}


}
48 changes: 48 additions & 0 deletions src/main/java/com/twilio/http/BearerTokenRequest.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
113 changes: 113 additions & 0 deletions src/main/java/com/twilio/http/OAuthHttpClient.java
Original file line number Diff line number Diff line change
@@ -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);

}
}
}
49 changes: 49 additions & 0 deletions src/main/java/com/twilio/http/OAuthRequest.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
89 changes: 89 additions & 0 deletions src/main/java/com/twilio/http/OAuthTokenManager.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading
Loading