I am writing code to use docusign demo machine through Docusign.esign.dll . I have tried using Oauth process for connecting the docusign.
I have used the code similar to the code motioned in here.
https://github.com/docusign/docusign-csharp-client/blob/master/test/SdkTests/JwtAuthUnitTests.cs
But I have used my demo machine Integetor key and private key. But I am getting the below error. So do I need to change any setup in my demo machine? Or how do I get valid Integotor key.
I hope my PEM key is causing the issue. So let me know how to preparte that pEM KEy.
I just copied by Private key and created the PEM file using notepad application.
Please let me know do I miss any thing?
Error calling Login: {\r\n \"errorCode\": \"PARTNER_AUTHENTICATION_FAILED\",\r\n \"message\": \"The specified Integrator Key was not found or is disabled. An Integrator key was not specified.\"\r\n}"}
BY default, the API points to their live/production servers. After creating an instance of the ApiClient, set it to point at the demo server:
apiClient.RestClient.BaseUrl = new Uri("https://demo.docusign.net/restapi");
Edit: That was for legacy authentication. For OAuth, please check to make sure you're pointing to account-d.docusign.com (notice the -d).
I too found this to be the issue, in the response the bearer token is missing
<br/><br/>string host = "https://demo.docusign.net/restapi/v2";
// Note with or without v2 their supplied credentials work<br/>
string oauthBasePath = "account-d.docusign.com";<br/>
ApiClient apiClient = new ApiClient(host);<br/>
apiClient.ConfigureJwtAuthorizationFlow(integratorKey, userId, oauthBasePath, privateKeyFilename, expiresInHours);
When you use the credentials from the JwtAuthUnitTests - TestConfig all works
Steps followed should be:
Created demo machine
Created IK
Created Secret key
Created RSA pair key
Copy the private key in to notepad and save that file in location
Missing steps are:
Granting Consent either using User Consent or Admin Consent, check
Service Integration for details.
Configure Redirect URI in the Integrator Key, only needed for User
Consent via Authorization Code Grant
You can use Admin Consent only if you can claim email domain in DocuSign else you need to use User Consent. With User Consent, normally using Authorization Code Grant, you need to get consent with scopes of Impersonation Signature. Once you have user's consent, then you can get new AccessToken for that user using JWT.
Also you need to point to correct host for Demo and Prod,
account-d.docusign.com is required for Demo
account.docusign.com is required for Prod
Above host is used to get access token from DocuSign Account Server (/oauth/token), and you will use above host also for getting the baseUri from /oauth/userinfo endpoint. Other than these two call, I don't think you will use above host.
In response for /oauth/userinfo endpoint call, you will get base_uri and account_id like below
"account_id": "fe0b61a3-3b9b-cafe-b7be-4592af32aa9b"
"base_uri": "https://demo.docusign.net"
You will use above base_uri and account_id for any other API calls, like for creating envelope etc
<base_uri>/restapi/v2/accounts/<account_Id>/envelopes
Related
I have migrating our Apis to use Docusign OAuth authentication flow. While testing I found that the code works perfectly fine when I point to Docusign Demo environment. However when I point to docusign Prod environment I get the following error.
The SSL connection could not be established, see inner exception.
There inner exception is actually null.
The docusign Auth call is fine and we get the accountId as expected. However the EnvelopesApi calls are failing. I do have a ticket open with Docusign but wanted to see if any one can help.
Our Api is deployed as a Windows Service and is in dotnet core 5.0. However I have tested this by deploying the Api to an IIS website with ssl binding and I can repro the same exception.
Auth Flow - OAuth JwtFlow
API BasePath as - "https : //docusign.net/restapi" (space added on purpose)
OAuth Base Path as - "account.docusign.com"
Below code get the accountId and sets the AccessToken. This is successful.
var privateKeyBytes = Encoding.UTF8.GetBytes(docuSignKey);
try
{
_tokenInfo = apiClient.RequestJWTUserToken(docusignConfig.IntegratorKey, docusignConfig.UserIdGuid,
docusignConfig.OAuthBasePath, privateKeyBytes, docusignConfig.TokenExpiryInHours);
var userInfo = apiClient.GetUserInfo(_tokenInfo?.access_token);
var account = userInfo?.Accounts
.FirstOrDefault(la => la.IsDefault.ToLowerInvariant() == true.ToString().ToLowerInvariant())
?? userInfo?.Accounts.First();
SetApiClientConfiguration(docusignConfig);
return account?.AccountId;
}
catch (Exception ex)
{
ESignLogger.Error($"{GetType().Name}.{nameof(AuthorizeAndGetAccountId)}. Error in getting account details. " + ex.Message);
return null;
}
Below code is the envelopesApi call which fails.
var recipientResponse = await _envelopesApi.ListRecipientsAsync(accountId, envelopeId);
The URLs for eSignature REST API calls for the DocuSign production environments can be different for different customers based on where their account is provisioned.
The default URL (https://www.docusign.net/restapi) can be used in some cases.
However, the best practice is to call the User Info endpoint for the particular user (it's by account, but a user can be a member of more than one account) and for each account that user is a member of, you'll get back a baseURI that can be different than the default I just posted above.
If this wasn't your issue, it may also be that you need to download one of the certificates to your server. You can find all of DocuSign SSL certificates in this page.
Steps to check:
OAuth step to obtain an access_token. You're using the JWT grant flow. If it returns an access token from account.docusign.com then you've succeeded. Note that your client ID (integration key) needs to pass go-live before it can be used with account.docusign.com
Next use the access token with the right base url for the eSig API. You can determine the right base url by using the /oauth/userinfo API. Or if your application is just for your own company, you can just look up your DocuSign account's base URL from the API & Keys page of the eSignature Settings (admin) tool.
Using the PHP API. App was working great on the developer side, now that I've gone "LIVE" and try to create/send the envelope I'm getting a 404 error.
I have:
verified the API integration key
changed the config settings for user id and client secret for the newly created Admin account
had the admin account take ownership of the app and granted consent
created new private and public keys, and verified that I'm pointing to the right files
verified that the claim is correct, particularly the aud parameter.
I ran get_userinfo to verify the API account id, sub user id, and production uri
I've got my JWT token
My create/send url:
https://na4.docusign.net/v2.1/accounts/a...z/envelopes
What have I missed?
As reported by the OP in a comment:
omitted "restapi" from my url
I am using docusign for digital signature , where I have to create a jwt token. For this I have been using code from git repo https://github.com/docusign/docusign-python-client.
docusign version 3.1.0 , python version 3.5 and 3.6 in sandbox mode .
getting following error
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='https', port=443): Max retries exceeded with url http://account-d.docusign.com/oauth/token (Caused by NewConnectionError(': Failed to establish a new connection: [Errno -2] Name or service not known',))
i have followed the curl request using following url JSON Web Token (JWT) Grant
got an application token and while passing in auth giving me 401(401 UNAUTHORIZED
) error
postman
claim = {"iss": '4556e2f7-4a3d-41f9-a0c3-18535a28ab2a', "aud": 'account-d.docusign.com', "iat": now, "exp": later, "scope": " ".join(scopes)} token = jwt.encode(payload=claim, key=pkey, algorithm='RS256').decode("utf-8")
i getting application token using above code but when i pass all the required parameter i got {"error":"invalid_grant","error_description":"no_valid_keys_or_signatures"} but when i pasted my jwt token in jwt.io with my public and privte key its says signature verified.
I have added the links of screenshots, that might also help to understand more about the problem.
https://ibb.co/2yKXNCW https://ibb.co/cFTk6R1 https://ibb.co/t3YMkr2
The concept is to generate a key with 3 elements (Header RS256 Algorith, the Payload Data that contains the application date and token validity and the private and public key. On the site jwt.io we will put all this information and the site will retour an Base64 Key we can further entered on
Open the JWT.IO site jwt.io
Choose Algorithm RS256 on the top
For the second Pane : “Payload: Data”
https://admindemo.docusign.com/apps-and-keys
iss :
The integration key (also known as client ID) of the application.
sub : The User ID of the user to be impersonated.
(The person granting permission must have a DocuSign user account.)
The provided User ID must be in GUID (not email) format. You can look up a user’s GUID from their email using the users.list method.
Note: If you don’t already have the User ID of the user to act on behalf of, you can obtain it by completing the Authorization Code Grant flow begun in the Request the authorization code step.
https://admindemo.docusign.com/apps-and-keys -> Apps and Keys ->
aud : The URI of the authentication service instance to be used.
For demo environments, use account-d.docusign.com.
For production environments, use account.docusign.com.
Important: Do not include https:// in the aud value!
iat : The date-time when the JWT was issued
in Unix epoch format. You must include an iat value when creating a JWT, typically set to the current time.
You can convert a date to linux epoch format with the following link : https://www.epochconverter.com/
exp: The date-time when the JWT assertion will expire
in Unix epoch format. Defaults to one hour from the value of iat and cannot be set to a greater value. You must include an exp value when creating a JWT.
Use the epoch https://www.epochconverter.com/
⚠ Please note that this value must not be too far in time, in our example we are using 14 years of validity.
scope : The scopes to request. In our example “signature impersonation”.
All the scopes are defined on the following site https://developers.docusign.com/platform/auth/reference/scopes/
{
"iss":"ea670856-0422-4d8e-99bd-80560d323639",
"sub":"917c9fc5-d615-43bb-8a28-87e7ede3b8ac",
"aud":"account-d.docusign.com",
"iat": 1628496664,
"exp":1912493464,
"scope":"signature impersonation"
}
For the Pane “Verify Signature”
https://admindemo.docusign.com/apps-and-keys -> Actions -> Edit
… -> Click on “Generate RSA”
Select the Public and Paste it on the section
And make the same for the public key. If everything is OK, you must have the Tag “Signature Verified” at the left of the document, see the picture bellow.
For the final step, you obtain the key for stating using the API
Go to Postman
https://www.postman.com/use-cases/api-testing-automation/
Sign in (for me with my google account)
You must fille the following values
Select “Post”
Put the API Value : https://account-d.docusign.com/oauth/token
Click on the “Body” section
Add the key “grant_type”
Add the value “urn:ietf:params:oauth:grant-type:jwt-bearer” for the key “grant_type”
Add the key “assertion”
Add the generate value from the site jwt on the previous steps
Finally Click on Send for obtain the “access_token” necessary the the further call.
[enter image description here][1]
Here are the things that you must ensure:
You have an Integration key (clientID) that is configured correctly.
You have an RSA Private key. that key was copied/pasted exactly as given to your configuration file. New lines must be preserved
Your URLs are matching the environment. Meaning you use account-d and demo.docusign.net for the sandbox env endpoints.
You need the userId which is a GUid for the user that would be impersonated. That user must consent to the application. You have to ensure you pass userId and not accountId and that it is for the same account that you would be using.
If you confirm all of this and still get an error - I would consider to use our code example to start. I don't see code in your question, but our code example should be a good way to start.
I am trying to generate a token for a user with below code.
string apiResourceId = "11224320-66b9-4132-8953-9aa485f07004";
string clientId = "bc9869a0-2393-4e42-8c52-845071640ea8";
Uri redirectUri = new Uri("https://localhost:44335/");
string authority = string.Format("https://login.windows.net/{0}",
"rudderless.onmicrosoft.com");
var authContext = new AuthenticationContext(authority);
AuthenticationResult authenticationResult;
authenticationResult = await authContext.AcquireTokenAsync(apiResourceId, clientId,
redirectUri, new PlatformParameters(PromptBehavior.Auto, null));
I have been getting an error in AcquireTokenAsync call -
AADSTS70002: The request body must contain the following parameter:
'client_secret or client_assertion'. Trace ID:
a198696d-8377-40eb-8351-527a25183500 Correlation ID:
24d4b47d-67bf-46c0-a6b7-a248c434512e Timestamp: 2017-09-20 23:09:38Z
Why do I need a client_secret or client_assertion if I want to generate a token when a user is authenticated against a AAD? The type of Client I am using is "Web app /API". However when I am trying to use a Native client I get the token generated but API call to apResourceID is generating unauthorized error.
Few Questions I am seeking help on related to the scinario -
Why I need to provide client_secret when I am using user auth flow?
Why AcquireToken succeed when I change the client Type to Native?
Why the token generated through native client gives an Unauthorize error?
Is there a way for admin to consent on behalf of every user in AAD?
Why I need to provide client_secret when I am using user auth flow?
Web Apps and APIs are considered Confidential Clients. See here for a definition of the different Client Types in the OAuth 2 Specification. These kinds of client always need to use their client secret to authenticate, no matter the flow they are following.
Confidential clients are typically issued (or establish) a set of
client credentials used for authenticating with the authorization
server (e.g., password, public/private key pair).
Why AcquireToken succeed when I change the client Type to Native?
Native Client Applications are a subset of Public Clients. These are defined, in the specification as:
Clients incapable of maintaining the confidentiality of their
credentials (e.g., clients executing on the device used by the
resource owner, such as an installed native application or a web
browser-based application), and incapable of secure client
authentication via any other means.
Therefore, they do not have or need a client_secret to authenticate... but this also means they can only authenticate with user context, whereas a confidential client could authenticate without a user present (Client Credential Flow).
Why the token generated through native client gives an Unauthorize
error?
This is hard to answer without knowing more about the error and the call you are making that causes this error. You should provide more information about this scenario.
Is there a way for admin to consent on behalf of every user
in AAD?
Yes. In the new Azure Active Directory V2 Endpoint, we have an "Admin Consent Endpoint".
Using the older V1 endpoint, we have an &prompt=admin_consent query string which you can read about here.
I am working on a project where we are going to be using different services in a microservice architecture, and we would like to also use some Firebase services. I am working on an auth server that is going to mint custom JWT's for use in both Firebase, as well as the other API projects.
We would like to use the Firebase Auth SDK to easily integrate with FB, Google, Twitter etc, but we need to enrich the user's token with more data. Therefore, my thought process is that I'd create a Node.JS auth server that uses the Firebase Admin SDK to do this. The flow would be as follows:
User logs in with favourite provider on client
If login is succesful, the user receives a JWT from Firebase. This is sent to the auth server for validation
If the auth server can validate the token using the admin SDK, create a new custom token enriched with more data, and return this new custom token to the client
Have client re-authenticate with the new custom token, and use it for communication with both Firebase as well as our other API projects (which will mainly be in .NET Core)
Step 1-3 works fine. The problem arises when trying to verify the custom token on the other services.
TL;DR : There are two questions inhere:
When validating custom tokens issued using the Firebase Node.JS Admin SDK, what should I use as the public key? A key extracted from Google's exposed JWK's, or a key extracted from the private key that is used to sign?
In case of the JWK approach, how should I construct the custom token with a kid header?
First, I am in doubt of the proper way to verify it. (Please excuse me, I'm not that experienced creating OAuth flows.) The algorithm used is RS256, so I should be able to verify the token using a public key. As I see it, there are two ways to get this key:
Extract the public key from the private key and verify using this. I can do this and verify successfully on a test endpoint on my auth server, however I feel this is the incorrect way to do it
The other, and more correct way I think, is to use the values from the token to find the JWK's on Google's "/.well-known/openid-configuration/" endpoint for my project, , i.e.
https: //securetoken.google.com/[PROJECT ID]/.well-known/openid-configuration
to retrieve the exponent and modulus for the correct kid (key ID) and create the public key from those.
The token generated from the admin SDK by doing
admin.auth().createCustomToken(uid, additionalClaims).then(function(customToken)
with some custom claims looks something like this:
headers:
{
"alg": "RS256",
"typ": "JWT"
}
payload:
{
"claims": {
"premiumAccount": true,
"someRandomInnerObject": {
"something": "somethingRandom"
}
},
"uid": "<uid for the user>",
"iat": 1488454663,
"exp": 1488458263,
"aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iss": "firebase-adminsdk-le7ge#<PROJECT ID>.iam.gserviceaccount.com",
"sub": "firebase-adminsdk-le7ge#<PROJECT ID>.iam.gserviceaccount.com"
}
I can't seem to get method 2 to work, though. One problem is that the generated token does not have a kid header, and so does not conform to the OpenID spec (AFAIK), which leads to one of two options:
Go with the first approach above. This leads to problems though - if I for some reason need to revoke or reset the private key on the auth server, I need to do it and deploy the changes on all the other services too, making the solution less dynamic and more error-prone.
Generate a similar token manually using one of the libs mentioned at jwt.io, and add the kid from the original Firebase ID token to it's headers.
Problems with number 2:
What should I put as iss, aud and sub, then? The same values as the admin SDK does? If so, isn't that 'cheating', as they are no longer the issuer?
I've tried it (generating a similar copy of the token, but adding the kid of the original token), and I can't seem to verify the generated token using the created PEM key for the kid.
The way I do the latter is this (following a blog guide on the subject):
Go to https://www.googleapis.com/service_accounts/v1/jwk/securetoken#system.gserviceaccount.com and retrieve the modulus (n) and exponent (e) for the relevant kid
Generate the public key using a lib (rsa-pem-from-mod-exp)
Use the key to verify using the 'official' jwt lib
The above results in a public key as such:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAxXpo7ChLMnv1QTovmm9DkAnYgINO1WFBWGAVRt93ajftPpVNcxMT
MAQI4Jf06OxFCQib94GyHxKDNOYiweVrHVYH9j/STF+xbQwiPF/8L7+haC2WXMl2
tkTgmslVewWuYwpfm4CoQFV29OVGWCqwEcbCaycWVddm1ykdryXzNTqfzCyrSZdZ
k0yoE0Q1GDcuUl/6tjH1gAfzN6c8wPvI2YDhc5gIHm04BcLVVMBXnC0hxgjbJbN4
zg2QafiUpICZzonOUbK6+rrIFGfHpcv8mWG1Awsu5qs33aFu1Qx/4LdMAuEsvX9f
EmFZCUS8+trilqJbcsd/AQ9eOZLAB0BdKwIDAQAB
-----END RSA PUBLIC KEY-----
Two things seem to be wrong. One is that the key is different from the one I can extract from the private key. The other is that the one I extract from the private key has these comments instead:
-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
with no 'RSA'. Does this matter? In any case, it doesn't verify.
Finally, did I misunderstand the OpenID flow completely? Are the JWKs generated from a private key that I need as well to verify my JWTs? Should I expose my own JWKs on my auth server for the other services to contact and use instead of Google's? I'm a bit confused as to what the Firebase Admin SDK does and doesn't do, I think :-)
I know this is a lot of questions, but I think they're all related.
Some resources I've relied on in my research (besides the official admin sdk docs ofcourse):
jwt.io
Is it still possible to do server side verification of tokens in Firebase 3?
https://ncona.com/2015/02/consuming-a-google-id-token-from-a-server/
https://stackoverflow.com/a/42410233/1409779
https://andrewlock.net/a-look-behind-the-jwt-bearer-authentication-middleware-in-asp-net-core/
After re-authenticating the Firebase client SDK with the custom token, the client actually generates a new ID token with the claims from the custom token. This ID token is what you should use to verify requests made to your different microservices (documented here). So yes, your original ID token is discarded, but a new one is created in its place. And that ID token will be automatically refreshed every hour. So, you should be able to just call user.getToken() to get a valid ID token whenever you need it. That method handles all the caching on your behalf.