Skip to content

Commit fd027d5

Browse files
author
Majid Mallis
committed
Added service integration OAuth support
1 parent dbb13ce commit fd027d5

File tree

14 files changed

+417
-106
lines changed

14 files changed

+417
-106
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@ See [DocuSign Support Center](https://support.docusign.com/en/releasenotes/) for
77
### Changed
88
- Updated the package with the latest API monthly release.
99

10+
11+
## [2.3.0] - 2017-07-27
12+
### Added
13+
- Support for DocuSign JWT OAuth for service integration (2-legged authentication)
14+
1015
## [2.2.1] - 2017-07-13
1116
### Fixed
12-
- unit tests under Java8 cmplain about AccessTokenListener being a nested class.
17+
- Unit tests under Java8 complain about AccessTokenListener being a nested class.
1318

1419
## [2.2.0] - 2017-06-17
1520
### Added
16-
- Added support for DocuSign OAuth
21+
- Support for DocuSign 3-legged OAuth
1722

1823
## [2.1.0] - 2017-03-09
1924
### Added

README.md

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Add this dependency to your project's POM:
2121
<dependency>
2222
<groupId>com.docusign</groupId>
2323
<artifactId>docusign-esign-java</artifactId>
24-
<version>2.2.1</version>
24+
<version>2.3.0</version>
2525
</dependency>
2626
```
2727

@@ -30,7 +30,7 @@ Add this dependency to your project's POM:
3030
Add this dependency to your project's build file:
3131

3232
```groovy
33-
compile "com.docusign:docusign-esign-java:2.2.1"
33+
compile "com.docusign:docusign-esign-java:2.3.0"
3434
```
3535

3636
#### Dependencies
@@ -72,14 +72,14 @@ android {
7272

7373
This client is available through the following Java package managers:
7474

75-
- [Nexus Repository Manager](https://oss.sonatype.org/#nexus-search;quick~docusign-esign-java) (oss.sonatype.org). You can search for com.docusign or docusign-esign-java. The current version is 2.2.1.
76-
- [JFrog Bintray](https://bintray.com/dsdevcenter/maven/docusign-esign-java) (bintray.com). You can search for com.docusign or docusign-esign-java. The current version is 2.2.1.
75+
- [Nexus Repository Manager](https://oss.sonatype.org/#nexus-search;quick~docusign-esign-java) (oss.sonatype.org). You can search for com.docusign or docusign-esign-java. The current version is 2.3.0.
76+
- [JFrog Bintray](https://bintray.com/dsdevcenter/maven/docusign-esign-java) (bintray.com). You can search for com.docusign or docusign-esign-java. The current version is 2.3.0.
7777

7878
### Others
7979

8080
Or you can manually download and add the following JARs to your project:
8181

82-
* The [docusign-esign-java-2.2.1](https://github.com/docusign/docusign-java-client/releases/latest) JAR.
82+
* The [docusign-esign-java-2.3.0](https://github.com/docusign/docusign-java-client/releases/latest) JAR.
8383
* The [Dependency JARs](/target/lib) in /lib folder.
8484

8585

@@ -192,32 +192,40 @@ public class DocuSignExample {
192192
}
193193
```
194194

195-
To send a signature request from a Template using username/password:
195+
To send a signature request from a Template using service integration:
196196

197197
```java
198198
import com.docusign.esign.api.*;
199199
import com.docusign.esign.client.*;
200200
import com.docusign.esign.model.*;
201201

202202
import java.util.List;
203+
import java.io.IOException;
203204

204205
public class DocuSignExample {
205206
public static void main(String[] args) {
206-
String username = "[EMAIL]";
207-
String password = "[PASSWORD]";
208-
String integratorKey = "[INTEGRATOR_KEY]";
207+
String OAuthBaseUrl = "account-d.docusign.com";
208+
String BaseUrl = "https://demo.docusign.net/restapi";
209+
String RedirectURI = "[OAUTH_REDIRECT_URI]";
210+
String IntegratorKey = "[INTEGRATOR_KEY]";
211+
String UserId = "[USER_ID_TO_SEND_ON_BEHALF]";
212+
String publicKeyFilename = "[PATH_TO_RSA265_PUBLIC_KEY]";
213+
String privateKeyFilename = "[PATH_TO_RSA265_PRIVATE_KEY]";
209214

210-
// initialize client for desired environment and add X-DocuSign-Authentication header
211-
ApiClient apiClient = new ApiClient("https://demo.docusign.net/restapi");
215+
ApiClient apiClient = new ApiClient(BaseUrl);
212216

213-
// configure 'X-DocuSign-Authentication' authentication header
214-
String authHeader = "{\"Username\":\"" + username + "\",\"Password\":\"" + password + "\",\"IntegratorKey\":\"" + integratorKey + "\"}";
215-
// If you have an OAuth access token stored in a variable named 'access_token', let's say, then instead, you can set authHeader as following (notice the extra space after 'Bearer'):
216-
// String authHeader = "{\"Bearer \":\"" + access_token + "\"}";
217-
apiClient.addDefaultHeader("X-DocuSign-Authentication", authHeader);
218-
Configuration.setDefaultApiClient(apiClient);
219-
try
220-
{
217+
try {
218+
// IMPORTANT NOTE:
219+
// the first time you ask for a JWT access token, you should grant access by making the following call
220+
// get DocuSign OAuth authorization url:
221+
//String oauthLoginUrl = apiClient.getJWTUri(IntegratorKey, RedirectURI, OAuthBaseUrl);
222+
// open DocuSign OAuth authorization url in the browser, login and grant access
223+
//Desktop.getDesktop().browse(URI.create(oauthLoginUrl));
224+
// END OF NOTE
225+
226+
apiClient.configureJWTAuthorizationFlow(publicKeyFilename, privateKeyFilename, OAuthBaseUrl, IntegratorKey, UserId, 3600); // request for a fresh JWT token valid for 1 hour
227+
Configuration.setDefaultApiClient(apiClient);
228+
221229
/////////////////////////////////////////////////////////////////////////////////////////////////////////
222230
// STEP 1: AUTHENTICATE TO RETRIEVE ACCOUNTID & BASEURL
223231
/////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -266,10 +274,10 @@ public class DocuSignExample {
266274

267275
// call the createEnvelope() API
268276
EnvelopeSummary envelopeSummary = envelopesApi.createEnvelope(accountId, envDef);
269-
}
270-
catch (com.docusign.esign.client.ApiException ex)
271-
{
277+
} catch (ApiException ex) {
272278
System.out.println("Exception: " + ex);
279+
} catch (Exception e) {
280+
System.out.println("Exception: " + e.getLocalizedMessage());
273281
}
274282
}
275283
}

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apply plugin: 'idea'
22
apply plugin: 'eclipse'
33

44
group = 'com.docusign'
5-
version = '2.1.0'
5+
version = '2.3.0'
66

77
buildscript {
88
repositories {

pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<artifactId>docusign-esign-java</artifactId>
66
<packaging>jar</packaging>
77
<name>docusign-esign-java</name>
8-
<version>2.2.1</version>
8+
<version>2.3.0</version>
99
<description>The official DocuSign eSignature JAVA client is based on version 2 of the DocuSign REST API and provides libraries for JAVA application integration. It is recommended that you use this version of the library for new development.</description>
1010
<url>https://www.docusign.com/developer-center</url>
1111

@@ -240,6 +240,13 @@
240240
<artifactId>org.apache.oltu.oauth2.client</artifactId>
241241
<version>${oltu-version}</version>
242242
</dependency>
243+
244+
<!-- JWT signing and verifying -->
245+
<dependency>
246+
<groupId>com.auth0</groupId>
247+
<artifactId>java-jwt</artifactId>
248+
<version>3.2.0</version>
249+
</dependency>
243250

244251
<!-- Base64 encoding that works in both JVM and Android -->
245252
<dependency>

src/main/java/com/docusign/esign/client/ApiClient.java

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package com.docusign.esign.client;
22

33
import com.fasterxml.jackson.annotation.*;
4+
import com.fasterxml.jackson.core.JsonParseException;
45
import com.fasterxml.jackson.databind.*;
56
import com.fasterxml.jackson.datatype.joda.*;
67
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
78

89
import com.sun.jersey.api.client.Client;
910
import com.sun.jersey.api.client.ClientResponse;
1011
import com.sun.jersey.api.client.GenericType;
12+
import com.sun.jersey.api.client.WebResource;
1113
import com.sun.jersey.api.client.config.DefaultClientConfig;
1214
import com.sun.jersey.api.client.filter.LoggingFilter;
15+
import com.sun.jersey.core.util.MultivaluedMapImpl;
1316
import com.sun.jersey.api.client.WebResource.Builder;
1417

1518
import com.sun.jersey.multipart.FormDataMultiPart;
@@ -20,28 +23,29 @@
2023
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
2124

2225
import javax.ws.rs.core.Response.Status.Family;
26+
import javax.ws.rs.core.UriBuilder;
2327
import javax.ws.rs.core.MediaType;
28+
import javax.ws.rs.core.MultivaluedMap;
2429

2530
import java.util.Collection;
26-
import java.util.Collections;
2731
import java.util.Map;
2832
import java.util.Map.Entry;
2933
import java.util.HashMap;
3034
import java.util.List;
3135
import java.util.ArrayList;
3236
import java.util.Date;
3337
import java.util.TimeZone;
34-
3538
import java.net.URLEncoder;
36-
3739
import java.io.File;
40+
import java.io.IOException;
3841
import java.io.UnsupportedEncodingException;
3942

4043
import java.text.DateFormat;
4144
import java.text.SimpleDateFormat;
4245

4346
import com.docusign.esign.client.auth.Authentication;
4447
import com.docusign.esign.client.auth.HttpBasicAuth;
48+
import com.docusign.esign.client.auth.JWTUtils;
4549
import com.docusign.esign.client.auth.ApiKeyAuth;
4650
import com.docusign.esign.client.auth.OAuth;
4751
import com.docusign.esign.client.auth.AccessTokenListener;
@@ -252,14 +256,20 @@ public void updateAccessToken() {
252256
* @param accessToken OAuth access token
253257
* @param expiresIn Validity period in seconds
254258
*/
255-
public void setAccessToken(String accessToken, Long expiresIn) {
259+
public void setAccessToken(final String accessToken, Long expiresIn) {
256260
for (Authentication auth : authentications.values()) {
257261
if (auth instanceof OAuth) {
258262
((OAuth) auth).setAccessToken(accessToken, expiresIn);
259263
return;
260264
}
261265
}
262-
throw new RuntimeException("No OAuth2 authentication configured!");
266+
addAuthorization("docusignAccessCode", new Authentication() {
267+
@Override
268+
public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams) {
269+
headerParams.put("Authorization", "Bearer " + accessToken);
270+
}
271+
});
272+
//throw new RuntimeException("No OAuth2 authentication configured!");
263273
}
264274

265275
/**
@@ -396,6 +406,65 @@ public void registerAccessTokenListener(AccessTokenListener accessTokenListener)
396406
}
397407
}
398408

409+
/**
410+
* Helper method to build the OAuth JWT grant uri (used once to get a user consent for impersonation)
411+
* @param clientId OAuth2 client ID
412+
* @param redirectURI OAuth2 redirect uri
413+
* @return the OAuth JWT grant uri as a String
414+
*/
415+
public String getJWTUri(String clientId, String redirectURI, String oAuthBasePath) {
416+
return UriBuilder.fromUri(oAuthBasePath)
417+
.scheme("https")
418+
.path("/oauth/auth")
419+
.queryParam("response_type", "code")
420+
.queryParam("scope", "signature%20impersonation")
421+
.queryParam("client_id", clientId)
422+
.queryParam("redirect_uri", redirectURI)
423+
.build().toString();
424+
}
425+
426+
/**
427+
* Configures the current instance of ApiClient with a fresh OAuth JWT access token from DocuSign
428+
* @param publicKeyFilename the filename of the RSA public key
429+
* @param privateKeyFilename the filename of the RSA private key
430+
* @param oAuthBasePath DocuSign OAuth base path (account-d.docusign.com for the developer sandbox
431+
and account.docusign.com for the production platform)
432+
* @param clientId DocuSign OAuth Client Id (AKA Integrator Key)
433+
* @param userId DocuSign user Id to be impersonated (This is a UUID)
434+
* @param expiresIn in seconds for the token time-to-live
435+
* @throws IOException if there is an issue with either the public or private file
436+
* @throws ApiException if there is an error while exchanging the JWT with an access token
437+
*/
438+
public void configureJWTAuthorizationFlow(String publicKeyFilename, String privateKeyFilename, String oAuthBasePath, String clientId, String userId, long expiresIn) throws IOException, ApiException {
439+
try {
440+
String assertion = JWTUtils.generateJWTAssertion(publicKeyFilename, privateKeyFilename, oAuthBasePath, clientId, userId, expiresIn);
441+
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
442+
form.add("assertion", assertion);
443+
form.add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
444+
445+
Client client = Client.create();
446+
WebResource webResource = client.resource("https://" + oAuthBasePath + "/oauth/token");
447+
ClientResponse response = webResource
448+
.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
449+
.post(ClientResponse.class, form);
450+
451+
ObjectMapper mapper = new ObjectMapper();
452+
JsonNode responseJson = mapper.readValue(response.getEntityInputStream(), JsonNode.class);
453+
if (!responseJson.has("access_token") || !responseJson.has("expires_in")) {
454+
throw new ApiException("Error while requesting an access token: " + responseJson);
455+
}
456+
String accessToken = responseJson.get("access_token").asText();
457+
expiresIn = responseJson.get("expires_in").asLong();
458+
setAccessToken(accessToken, expiresIn);
459+
} catch (JsonParseException e) {
460+
throw new ApiException("Error while parsing the response for the access token.");
461+
} catch (JsonMappingException e) {
462+
throw e;
463+
} catch (IOException e) {
464+
throw e;
465+
}
466+
}
467+
399468
/**
400469
* Parse the given string into Date object.
401470
*/

0 commit comments

Comments
 (0)