Technical Articles
Authenticate Docusign APIs By JWT OAuth Grant Mechanism In Cloud Integration
Docusign is a digital transaction platform that lets users send, sign and manage legal documents securely in the cloud. Docusign has the following authentication mechanism currently supported.
- Authorization code grant
- Implicit Grant
- JSON Web Token Grant
In this post, I will be demonstrating the generation of Docusign OAuth using JWT grant mechanism in cloud Integration tool. Advantages of this technique includes RSA key pair consisting of public-private key pair which provides great data security and management of large users at organizational level. The prerequisites before token generation includes following steps to be completed in Docusign Admin console.
- Generation of Integrator key also known as Client ID(iss).
- GUID format of user granting access permissions(sub).
- RSA public & private keys generation.
The integration flow for this process is given below.
Integration Flow
The integration flow consists of 4 basic steps.
- In the first step, the token properties are stored in the message property which consists of public and private key in plain text format, iss, sub, aud, exp and scope as signature impersonation as shown below.
- In the second step, there is a groovy code to generate JSON web token by utilizing java.security libraries as shown below.
import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; import java.io.IOException; import java.io.BufferedReader; import java.io.FileReader; import java.util.Date; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.X509EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec; import org.apache.commons.codec.binary.Base64; import com.auth0.jwt.JWT; import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.algorithms.Algorithm; def Message processData(Message message) { map = message.getProperties(); exp = map.get("exp"); aud = map.get("aud"); scope = map.get("scope"); iss = map.get("iss"); subject = map.get("subject"); accountNo = map.get("accountNo"); contentType = map.get("contentType"); accept = map.get("accept"); privateKey = map.get("privateKey"); publicKey = map.get("publicKey"); java.security.Security.addProvider( new org.bouncycastle.jce.provider.BouncyCastleProvider() ); // Read private key from property RSAPrivateKey privKey = getPrivateKey(privateKey); // Read public key from property RSAPublicKey pubKey = getPublicKey(publicKey); // Create RSA algorithm from keys Algorithm algorithm = Algorithm.RSA256(pubKey, privKey); // Get epoch time long currentTimeMs = System.currentTimeMillis(); Date issuedAt = new Date(currentTimeMs); // Get expiration time with validity of 1 hour long expiryTimeMs = currentTimeMs + Integer.parseInt(exp) * 3600000; Date expiresAt = new Date(expiryTimeMs); // Create JWT for impersonation String token = JWT.create() .withIssuer(iss) .withSubject(subject) .withIssuedAt(issuedAt) .withExpiresAt(expiresAt) .withAudience(aud) .withClaim("scope", scope) .sign(algorithm); message.setBody(token) return message; } public static RSAPrivateKey getPrivateKey(String privKey) throws IOException, GeneralSecurityException { return getPrivateKeyFromProperty(privKey); } public static RSAPrivateKey getPrivateKeyFromProperty(String key) throws IOException, GeneralSecurityException { String privateKey = key; privateKey = privateKey.replace("-----BEGIN RSA PRIVATE KEY-----", ""); privateKey = privateKey.replace("-----END RSA PRIVATE KEY-----", ""); byte[] encoded = Base64.decodeBase64(privateKey); KeyFactory kf = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); RSAPrivateKey privKey = (RSAPrivateKey) kf.generatePrivate(keySpec); return privKey; } public static RSAPublicKey getPublicKey(String pubKey) throws IOException, GeneralSecurityException { return getPublicKeyFromProperty(pubKey); } public static RSAPublicKey getPublicKeyFromProperty(String key) throws IOException, GeneralSecurityException { String publicKey = key; publicKey = publicKey.replace("-----BEGIN PUBLIC KEY-----", ""); publicKey = publicKey.replace("-----END PUBLIC KEY-----", ""); byte[] encoded = Base64.decodeBase64(publicKey); KeyFactory kf = KeyFactory.getInstance("RSA"); RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded)); return pubKey; }
Libraries
- Trigger a POST HTTP request to the Docusign authentication service with the following message body where ${in.body} is the JWT token generated from previous groovy step grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${in.body}. Add a Content-Type header with value application/x-www-form-urlencoded.
- Request-Reply step to Docusign authentication service as shown below.
OAuth Token Response
JWT Token
jwt.io
So in this post we have learnt how we can generate JWT token, which we can further use to generate OAuth tokens to authenticate Docusign APIs. In the next article I will be demonstrating the steps as part of calling the envelopes API which is used in signing process for documents.
Following are the links for reference.
- Docusign JWT Grant Documentation – https://developers.docusign.com/platform/auth/jwt/jwt-get-token/
- JWT Debugger – https://jwt.io/
- Integrator Key – https://www.youtube.com/watch?v=GgDqa7-L0yo
- Docusign Trial Account – https://go.docusign.com/o/trial/
- SAP BTP Products Trial – https://www.sap.com/products/free-trials