简体   繁体   中英

How to get DocuSign Embedded Signing works when Frontend and Backend are on different port

I am working on a project to integrate DocuSign embedding signing ceremony function. My frontend is Angular 6 (Port: 4200) and it is running on a different port as to the backend, NodeJS, (Port: 3000).

The frontend will call the backend (REST API) to generate the PDF file. Upon completed, it will trigger a redirect to the embeddedsigning route to connect to DocuSign to get the PDF file sign.

Upon successfully generated the PDF file, the '/api/docusign/signing' route is triggered (refer to below code).

...
    res.redirect('/api/docusign/signing'); 
...

The callback does not seem to work as it send the response back to the frontend which is not my intention. The callback is required so that the PDF is uploaded to the DocuSign and trigger the DocuSign to prompt signer to sign the document. Upon completed, the backend will update the database before pass back to the frontend.

I am still learning and experience NodeJS and I not sure how to go about it. Anyone has any idea or has such an experience is able to share how to go about it. Appreciate for your help. Thanks.

docusign-routes.js

module.exports = (app) => {
    app.get('/api/docusign/auth', docuSignController.authenticate);
    app.get('/api/docusign/callback', [docuSignController.dsLoginCB1, docuSignController.dsLoginCB2]);
    app.get('/api/docusign/signing', docuSignController.requireDocuSignToken,
        docuSignController.embeddedsigning);
}

docuSignController.js

    exports.authenticate = (req, res, next) => {
        passport.authenticate('docusign')(req, res, next);
    };

    exports.requireDocuSignToken = (req, res, next) => {
        let tokenBufferMin = 30;
        let now = moment();

        console.log('requireDocuSignToken');

        if (tokenBufferMin && req.docusign && req.docusign.accessToken &&
            now.add(tokenBufferMin, 'm').isBefore(req.docusign.expires)) {
            console.log('\nUsing existing access token');
            next();
        } else {
            console.log('\nGet a new access token');
            res.redirect('http://localhost:3000/api/docusign/auth');
        }
   };

   exports.dsLoginCB1 = (req, res, next) => {
       passport.authenticate('docusign', {failureRedirect: '/api/docusign/auth'})(req, res, next);
   };

   exports.dsLoginCB2 = (req, res, next) => {
       res.redirect('/api/docusign/signing');
   }

   exports.embeddedsigning = (req, res, next) => {
    ...
   }

Your code extract shown in your question does not show your embeddedsigning method.

What that method should do:

  1. It already knows that it has a good DocuSign API token.
  2. It should call the DocuSign EnvelopeViews::createRecipient method. The returnUrl parameter can either be a URL for your Angular front end or for your Node backend. That depends on your app architecture. Either should work.
  3. Redirect the user's browser to the url element in the DocuSign API call's response.

You can see an example of this in a React/Redux example I wrote. In this example, the React app is calling the DocuSign API directly (using a private CORS gateway). See lines 31 - 43 of file sendSignEnv.js The redirect is done on line 43.

From Node.JS, do a redirect as follows:

    res.redirect(results.url);

Here is an example from forthcoming code example:

// Step 3. create the recipient view, the Signing Ceremony
let viewRequest = makeRecipientViewRequest(envelopeArgs)
  , createRecipientViewP = req.dsAuthCodeGrant.makePromise(
        envelopesApi, 'createRecipientView')
  ;
// call the CreateRecipientView API
results = null; // reset
try {
    results = await createRecipientViewP(accountId, envelopeId,
        {recipientViewRequest: viewRequest});
} 
catch (error) {
    let errorBody = error && error.response && error.response.body
        // we can pull the DocuSign error code and message from the response body
      , errorCode = errorBody && errorBody.errorCode
      , errorMessage = errorBody && errorBody.message
    res.render('pages/error', {err: error, errorCode: errorCode, errorMessage: errorMessage});
}
if (!results) {return}

// Step 4. Redirect the user to the Signing Ceremony
// Don't use an iFrame!
// State can be stored/recovered using the framework's session or a
// query parameter on the returnUrl (see the makeRecipientViewRequest method)
res.redirect(results.url);

Here is the makeRecipientViewRequest

function makeRecipientViewRequest(args) {
    let viewRequest = new docusign.RecipientViewRequest();

    // Set the url where you want the recipient to go once they are done signing
    // should typically be a callback route somewhere in your app.
    // The query parameter is included as an example of how
    // to save/recover state information during the redirect to
    // the DocuSign signing ceremony. It's usually better to use
    // the session mechanism of your web framework. Query parameters
    // can be changed/spoofed very easily.
    viewRequest.returnUrl = dsReturnUrl + "?state=123";

    // How has your app authenticated the user? In addition to your app's
    // authentication, you can include authenticate steps from DocuSign.
    // Eg, SMS authentication
    viewRequest.authenticationMethod = 'none';

    // Recipient information must match embedded recipient info
    // we used to create the envelope.
    viewRequest.email = args.signerEmail;
    viewRequest.userName = args.signerName;
    viewRequest.clientUserId = args.signerClientId;

    // DocuSign recommends that you redirect to DocuSign for the
    // Signing Ceremony. There are multiple ways to save state.
    // To maintain your application's session, use the pingUrl
    // parameter. It causes the DocuSign Signing Ceremony web page
    // (not the DocuSign server) to send pings via AJAX to your
    // app,
    viewRequest.pingFrequency = 600; // seconds
    // NOTE: The pings will only be sent if the pingUrl is an https address
    viewRequest.pingUrl = dsPingUrl; // optional setting

    return viewRequest
}

Here is the makePromise code:

const {promisify} = require('util'); 
// See http://2ality.com/2017/05/util-promisify.html

/**
 * Returns a promise method, {methodName}_promise, that is a
 * promisfied version of the method parameter.
 * The promise method is created if it doesn't already exist.
 * It is cached via attachment to the parent object.
 * @function
 * @param obj An object that has method methodName
 * @param methodName The string name of the existing method
 * @returns {promise} a promise version of the <tt>methodName</tt>.
 */
DSAuthCodeGrant.prototype.makePromise = function _makePromise(obj, methodName){
  let promiseName = methodName + '_promise';
  if (!(promiseName in obj)) {
    obj[promiseName] = promisify(obj[methodName]).bind(obj)
  }
  return obj[promiseName]
}

Added

(In response to a comment, see below.)

I think that you should use passport from your backend. That way passport / DocuSign redirect will go to your backend.

You're running into CORS issues since you're trying to do too much from your frontend.

If you want to write more of a pure front end DocuSign app, you can, but you'll need a private CORS gateway. See my blog post part 1 , part 2 , and part 3

NOTE: for a front end app you must use Implicit Grant, not Authorization Code Grant.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM