Reputation: 39
I'm trying to implement Service Integration Authentication - Admin Consent for my application. Here's how I created the jwt:
class Program
{
static void Main(string[] args)
{
#region Test
string integratorKey = "integratorKey ";
string userId = "userId ";
string serverAddress = "serverAddress";
string scope = "signature";
string key = @"C:\Users\Tester\Desktop\privatekey.txt";
// JWT Header
// The header specfies the token type and the signature algorithm
var jwtHeader = new JwtHeader
{
{ "typ ", "JWT "},
{ "alg", "RS256"},
};
// JWT Body
// The body specfies the account and user id granting consen
var jwtPayload = new JwtPayload
{
{ "iss ", integratorKey},
{ "sub", userId},
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds()},
{ "exp", DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds()},
{ "aud", serverAddress},
{ "scope", scope}
};
// JWT Signature
// The body contains the result of signing the base64url-encoded header and body
string pemKey = File.ReadAllText(key);
var rsa = CreateRSAKeyFromPem(pemKey);
RsaSecurityKey rsaKey = new RsaSecurityKey(rsa);
var jwtSecurityToken = new JwtSecurityToken(jwtHeader, jwtPayload);
jwtSecurityToken.SigningKey = rsaKey;
// Token to String so you can use it in your client
var jwtHandler = new JwtSecurityTokenHandler();
var tokenString = jwtHandler.WriteToken(jwtSecurityToken);
#endregion
}
public static RSA CreateRSAKeyFromPem(string key)
{
TextReader reader = new StringReader(key);
PemReader pemReader = new PemReader(reader);
object result = pemReader.ReadObject();
if (result is AsymmetricCipherKeyPair)
{
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)result;
return DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)keyPair.Private);
}
else if (result is RsaKeyParameters)
{
RsaKeyParameters keyParameters = (RsaKeyParameters)result;
return DotNetUtilities.ToRSA(keyParameters);
}
throw new Exception("Unepxected PEM type");
}
}
I have verified the token generated by this code on jwt.io and all looks good. However when I tried to exchange a generated jwt for an access code using Postman, I will always get "invalid_grant", here's how I made the request in postman:
POST https://account-d.docusign.com/oauth/token
-Headers: Content-Type application/x-www-form-urlencoded
-Body: grant_type urn:ietf:params:oauth:grant-type:jwt-bearer assertion [Generated jwt]
I even tried putting
Headers: Authorization Basic b64encoded(integratorKey:secretKey)
as well but still no luck.
Could you please point out what I'm doing wrong here? Thanks :)
Upvotes: 0
Views: 703
Reputation: 39
Ok, I have managed to figure out what were wrong myself and I can now receive the access token in Postman. 3 things that I have changed to make this work:
When I Authorize Application from Organization Portal, I have assigned the application to an incorrect Integrator Key so I need to change that to point to the correct one.
I found in the C# SDK the code to generate jwt so I used that to make sure the jwt created is accepted by DocuSign:
static void Main(string[] args)
{
string integratorKey = "integratorKey ";
string userId = "userId ";
string serverAddress = "account-d.docusign.com";
string scope = "signature impersonation";
string privateKeyFilename = @"C:\Users\Tester\Desktop\privatekey.txt";
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
SecurityTokenDescriptor descriptor = new SecurityTokenDescriptor()
{
IssuedAt = DateTime.UtcNow,
Expires = DateTime.UtcNow.AddHours(1)
};
descriptor.Subject = new ClaimsIdentity();
descriptor.Subject.AddClaim(new Claim("scope", scope));
descriptor.Subject.AddClaim(new Claim("aud", serverAddress));
descriptor.Subject.AddClaim(new Claim("iss", integratorKey));
if (userId != null)
{
descriptor.Subject.AddClaim(new Claim("sub", userId));
}
if (privateKeyFilename != null)
{
string pemKey = File.ReadAllText(privateKeyFilename);
var rsa = CreateRSAKeyFromPem(pemKey);
RsaSecurityKey rsaKey = new RsaSecurityKey(rsa);
descriptor.SigningCredentials = new SigningCredentials(rsaKey, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.HmacSha256Signature);
}
var token = handler.CreateToken(descriptor);
string jwtToken = handler.WriteToken(token);
}
public static RSA CreateRSAKeyFromPem(string key)
{
TextReader reader = new StringReader(key);
PemReader pemReader = new PemReader(reader);
object result = pemReader.ReadObject();
if (result is AsymmetricCipherKeyPair)
{
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)result;
return DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)keyPair.Private);
}
else if (result is RsaKeyParameters)
{
RsaKeyParameters keyParameters = (RsaKeyParameters)result;
return DotNetUtilities.ToRSA(keyParameters);
}
throw new Exception("Unepxected PEM type");
}
After I have made change 1 and 2, I still have an error in Postman: "consent_required". Turns out I have to add a redirect uri to my integrator key then navigate to this url:
SERVER/oauth/auth?response_type=code&scope=signature%20impersonation&client_id=CLIENT_ID&redirect_uri=https://docusign.com
and click "Grant".
Hope this helps.
Upvotes: 3