Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
*.ear
*.project
*.classpath
.idea
/.settings
/target
bin/
src/test/*
*.iml
217 changes: 159 additions & 58 deletions src/main/java/com/illumina/basespace/ApiClientManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,15 @@
public final class ApiClientManager
{
private static Logger logger = Logger.getLogger(ApiClientManager.class.getPackage().getName());
private static final ObjectMapper mapper = new ObjectMapper();

private static final ApiClientManager singletonObject = new ApiClientManager();


private static Client httpClient = createHttpClient();
private static final String AUTHORIZATION_PENDING_ERROR_TYPE = "authorization_pending";

private ApiClientManager()
{


}
public static synchronized ApiClientManager instance()
Expand Down Expand Up @@ -82,99 +86,104 @@ protected String requestAccessToken(ApiConfiguration configuration)
{
return configuration.getAccessToken();
}


AuthVerificationCode authCode = getAuthVerificationCode(configuration);

String uri = authCode.getVerificationWithCodeUri();
BrowserLaunch.openURL(uri);

return startAccessTokenPolling(authCode, configuration);
}
catch(BaseSpaceException bs)
{
throw bs;
}
catch(Throwable t)
{
t.printStackTrace();
throw new RuntimeException("Error requesting access token from BaseSpace: " + t.getMessage());
}
}

/**
* Get the verification code and device code
*
* This API corresponds to the following step in the authentication flow:
* https://developer.basespace.illumina.com/docs/content/documentation/authentication/obtaining-access-tokens#Gettingtheverificationcodeanddevicecode
*
* @param configuration configuration for the session
* @return an auth verification code
* @throws AccessDeniedException if an auth verification token could not be obtained from BaseSpace
*/
public AuthVerificationCode getAuthVerificationCode(ApiConfiguration configuration) throws AccessDeniedException {
try
{
Form form = new Form();
form.add("client_id", configuration.getClientId());
form.add("scope", configuration.getAuthorizationScope());
form.add("response_type", "device_code");

Client client = Client.create(new DefaultClientConfig());
client.addFilter(new ClientFilter()
{
@Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException
{
logger.fine(request.getMethod() + " to " + request.getURI().toString());
ClientResponse response = null;
try
{
response = getNext().handle(request);
}
catch(ClientHandlerException t)
{
throw new BaseSpaceException(t.getMessage(), t,request.getURI());
}
return response;
}
});

Client client = getHttpClient();

WebResource resource = client.resource(UriBuilder.fromUri(configuration.getApiRootUri())
.path(configuration.getVersion())
.path(configuration.getAuthorizationUriFragment())
.build());
logger.finer(resource.toString());

ClientResponse response = resource.accept(
MediaType.APPLICATION_XHTML_XML,
MediaType.APPLICATION_FORM_URLENCODED,
MediaType.APPLICATION_JSON)
.post(ClientResponse.class,form);
String responseAsJSONString = response.getEntity(String.class);
logger.finer(responseAsJSONString);

final ObjectMapper mapper = new ObjectMapper();

AuthVerificationCode authCode = mapper.readValue(responseAsJSONString, AuthVerificationCode.class);
if (authCode.getError() != null)
{
throw new BaseSpaceException(authCode.getErrorDescription(),null,resource.getURI());
}

logger.finer(authCode.toString());

String uri = authCode.getVerificationWithCodeUri();
BrowserLaunch.openURL(uri);

return authCode;
}
catch(BaseSpaceException bs)
{
throw bs;
}
catch(Throwable t)
{
t.printStackTrace();
throw new RuntimeException("Error requesting auth verification code from BaseSpace: " + t.getMessage());
}
}

protected String startAccessTokenPolling(AuthVerificationCode authCode, ApiConfiguration configuration){
try {
//Poll for approval
form = new Form();
Form form = new Form();
form.add("client_id", configuration.getClientId());
form.add("client_secret",configuration.getClientSecret());
form.add("code",authCode.getDeviceCode());
form.add("grant_type","device");

resource = client.resource(UriBuilder.fromUri(configuration.getApiRootUri())

Client client = getHttpClient();

WebResource resource = client.resource(UriBuilder.fromUri(configuration.getApiRootUri())
.path(configuration.getVersion())
.path(configuration.getAccessTokenUriFragment())
.build());

String accessToken = null;
while(accessToken == null)
{
long interval = authCode.getInterval() * 1000;
Thread.sleep(interval);
response = resource.accept(
MediaType.APPLICATION_XHTML_XML,
MediaType.APPLICATION_FORM_URLENCODED,
MediaType.APPLICATION_JSON)
.post(ClientResponse.class,form);

responseAsJSONString = response.getEntity(String.class);
logger.finer(responseAsJSONString);
if(response.getClientResponseStatus().getStatusCode() > 400)
{
AccessToken error = mapper.readValue(responseAsJSONString, AccessToken.class);
throw new BaseSpaceException(resource.getURI(),error.getErrorDescription(),response.getClientResponseStatus().getStatusCode());
}
if(response.getClientResponseStatus().getStatusCode() == 200)
{

AccessToken token = mapper.readValue(responseAsJSONString, AccessToken.class);
accessToken = token.getAccessToken();
}
}
accessToken = getAccessToken(resource, form);
}
return accessToken;

}
catch(BaseSpaceException bs)
} catch(BaseSpaceException bs)
{
throw bs;
}
Expand All @@ -184,5 +193,97 @@ public ClientResponse handle(ClientRequest request) throws ClientHandlerExceptio
throw new RuntimeException("Error requesting access token from BaseSpace: " + t.getMessage());
}
}

/**
* Perform a single polling attempt to get access token. This function should be called periodically while awaiting user authorization.
*
* This API corresponds to the following step in the authentication flow:
* https://developer.basespace.illumina.com/docs/content/documentation/authentication/obtaining-access-tokens#Gettingtheaccesstokenfornonweb-basedapps
*
* @param deviceCode device code received from the previous step {@link this#getAuthVerificationCode}
* @param configuration configuration for the session
* @return an access token or null if the user has not yet approved the access request
* @throws AccessDeniedException if an auth verification token could not be obtained from BaseSpace
*/
public String getAccessToken(String deviceCode, ApiConfiguration configuration) throws BaseSpaceException {
Form form = new Form();
form.add("client_id", configuration.getClientId());
form.add("client_secret",configuration.getClientSecret());
form.add("code",deviceCode);
form.add("grant_type","device");

Client client = getHttpClient();
WebResource resource = client.resource(UriBuilder.fromUri(configuration.getApiRootUri())
.path(configuration.getVersion())
.path(configuration.getAccessTokenUriFragment())
.build());
return getAccessToken(resource, form);
}

private String getAccessToken(WebResource resource, Form form) throws BaseSpaceException {
try {
ClientResponse response = resource.accept(
MediaType.APPLICATION_XHTML_XML,
MediaType.APPLICATION_FORM_URLENCODED,
MediaType.APPLICATION_JSON)
.post(ClientResponse.class, form);

String responseAsJSONString = response.getEntity(String.class);
logger.finer(responseAsJSONString);
String accessToken = null;
if (response.getClientResponseStatus().getStatusCode() >= 400) {
AccessToken error = mapper.readValue(responseAsJSONString, AccessToken.class);
if(!error.getError().equals(AUTHORIZATION_PENDING_ERROR_TYPE)){
throw new BaseSpaceException(resource.getURI(), error.getErrorDescription(), error.getError(), response.getClientResponseStatus().getStatusCode());
}
}
if (response.getClientResponseStatus().getStatusCode() == 200) {

AccessToken token = mapper.readValue(responseAsJSONString, AccessToken.class);
accessToken = token.getAccessToken();
}
return accessToken;
} catch(BaseSpaceException bs)
{
throw bs;
}
catch(Throwable t)
{
t.printStackTrace();
throw new RuntimeException("Error polling for access token from BaseSpace: " + t.getMessage());
}
}

private static Client createHttpClient(){
try {
Client client = Client.create(new DefaultClientConfig());
client.addFilter(new ClientFilter() {
@Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
logger.fine(request.getMethod() + " to " + request.getURI().toString());
ClientResponse response = null;
try {
response = getNext().handle(request);
} catch (ClientHandlerException t) {
throw new BaseSpaceException(t.getMessage(), t, request.getURI());
}
return response;
}
});
return client;
} catch(Throwable t){
return null;
}
}

private Client getHttpClient() {
if (httpClient == null) {
httpClient = createHttpClient();
if (httpClient == null) {
throw new RuntimeException("Error creating Web Client");
}
}
return httpClient;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class BaseSpaceException extends RuntimeException
{
private URI uri;
private int errorCode;
private String errorType;

public BaseSpaceException(String message)
{
Expand All @@ -52,6 +53,11 @@ public BaseSpaceException(URI uri,String message,int errorCode)
{
this(message,null,uri,errorCode);
}

public BaseSpaceException(URI uri, String message, String errorType, int errorCode){
this(uri, message, errorCode);
this.errorType = errorType;
}

public BaseSpaceException(String message,Throwable cause,URI uri,int errorCode)
{
Expand Down Expand Up @@ -81,6 +87,10 @@ public int getErrorCode()
return errorCode;
}

public String getErrorType(){
return errorType;
}

@Override
public String getMessage()
{
Expand Down