User required to sign in before signing document - node.js

I am redirecting the user to docusign to sign their document. This is done with docusigns view request.
I would like the user to be required to sign in before signing.
I have been unable to find this in docusign documentation.
I did find this old stack overflow question Requiring DocuSign login to sign a document but the first link does not work.
Below is my code for the envelope creation
function makeEnvelope(args){
const env = new docusign.EnvelopeDefinition();
env.emailSubject = 'Please sign this document set';
// add 1 day reminder
const notification = new docusign.Notification();
notification.useAccountDefaults = 'false';
const reminders = new docusign.Reminders();
reminders.reminderEnabled = 'true';
reminders.reminderDelay = '1';
reminders.reminderFrequency = '1';
notification.reminders = reminders;
env.notification = notification;
const doc1 = new docusign.Document();
doc1.documentBase64 = Buffer.from(htmlPage(args.htmlArgs)).toString('base64');
doc1.name = args.documentName;
doc1.fileExtension = 'html';
doc1.documentId = '1';
env.documents = [doc1];
const signer1 = docusign.Signer.constructFromObject({
email: args.htmlArgs.submitterEmail,
name: args.htmlArgs.submitterName,
clientUserId: 1,
recipientId: 1,
routingOrder: 1 });
// Signer 2 is the supervisor. Gets sent the document after signer 1 signs
const signer2 = docusign.Signer.constructFromObject({
email: args.htmlArgs.supervisorEmail,
name: args.htmlArgs.supervisorName,
recipientId: 2,
routingOrder: 2 });
const signHere1 = docusign.SignHere.constructFromObject({
anchorString: '**signature_1**',
anchorYOffset: '10', anchorUnits: 'pixels',
anchorXOffset: '20'});
const signHere2 = docusign.SignHere.constructFromObject({
anchorString: '**signature_2**',
anchorYOffset: '10', anchorUnits: 'pixels',
anchorXOffset: '20'});
// Tabs are set per recipient / signer
signer1.tabs = docusign.Tabs.constructFromObject({
signHereTabs: [signHere1]});
signer2.tabs = docusign.Tabs.constructFromObject({
signHereTabs: [signHere2]});
env.recipients = docusign.Recipients.constructFromObject({
signers: [signer1, signer2],
});
env.status = args.status;
return env;
}
Here is the view request code
recipientView.controller = async (args) => {
const viewRequest = new docusign.RecipientViewRequest();
viewRequest.returnUrl = args.dsReturnUrl;
viewRequest.authenticationMethod = 'none';
viewRequest.email = args.signerEmail;
viewRequest.userName = args.signerName;
viewRequest.clientUserId = args.signerClientId;
viewRequest.authenticationMethod = 'Email';
return await recipientView.worker(viewRequest, args);
}
recipientView.worker = async (viewRequest, args) =>{
const dsApiClient = new docusign.ApiClient();
dsApiClient.setBasePath(args.basePath);
dsApiClient.addDefaultHeader('Authorization', 'Bearer ' + args.accessToken);
const envelopesApi = new docusign.EnvelopesApi(dsApiClient)
const results = await envelopesApi.createRecipientView(args.accountId, args.envelopeId, {recipientViewRequest: viewRequest});
return results.url;
}
I have tried different values for viewRequest.authenticationMethod
Nothing seems to work.

If you want signers to authenticate to DocuSign - they must have a membership in some DocuSign account. You cannot know or check if they do, so this method is not useful unless the envelopes are only sent within an organization to a fixed number of individuals that you know are members of the DocuSign account.
However, there are other ways to require recipient authentication and increase security over email. You can use SMS authentication, KBI or IDV for example. Read about how to use them in this blog post about recipient authentication.

Related

DocuSign createEnvelope Request Failed with 404 Error

I have been trying to create an envelope from a template using the Docusign SDK in Node.js, but no matter what API I try run, I get the error 404, with this Html code as a response.
I am including the code here:
const {email, name, templateId} = body;
const token = await DocuSign.token();
const dsAPIClient = new ApiClient();
dsAPIClient.setBasePath(DOCUSIGN_BASE_PATH as string);
dsAPIClient.addDefaultHeader('Authorization', `Bearer ${token}`);
const envelopeApi = new docusign.EnvelopesApi(dsAPIClient);
const envelope = this.makeEnvelope({email, name, templateId});
console.log(envelope);
// const result = await envelopeApi.createEnvelope(ACCOUNT_ID as string, {
// envelopeDefinition: {},
// });
const result = await axios.post(
`${DOCUSIGN_BASE_PATH}/v2.1/accounts/${ACCOUNT_ID}/envelopes`,
envelope,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
// console.log('This is the result', result);
return result.data;
This is the error that I am getting, I get the same error on SDK and Axios both.
Symbol(kOutHeaders)]: [Object: null prototype]
' <h2>404 - File or directory not found.</h2>\r\n' +
' <h3>The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable.</h3>\r\n' +
' </fieldset></div>\r\n' +
'</div>\r\n' +
'</body>\r\n' +
'</html>\r\n'
},
isAxiosError: true,
toJSON: [Function: toJSON]
Your code has the SDK code commented, so I'm not sure why you did this.
Here is code that works, you can find it in GitHub.
An even easier option is to use the quickstart.
let dsApiClient = new docusign.ApiClient();
dsApiClient.setBasePath(args.basePath);
dsApiClient.addDefaultHeader("Authorization", "Bearer " + args.accessToken);
let envelopesApi = new docusign.EnvelopesApi(dsApiClient),
results = null;
let envelope = makeEnvelope(args.envelopeArgs);
function makeEnvelope(args) {
let docPdfBytes;
docPdfBytes = fs.readFileSync(args.docFile);
let env = new docusign.EnvelopeDefinition();
env.emailSubject = "Please sign this document";
let doc1 = new docusign.Document(),
doc1b64 = Buffer.from(docPdfBytes).toString("base64");
doc1.documentBase64 = doc1b64;
doc1.name = "Lorem Ipsum"; // can be different from actual file name
doc1.fileExtension = "pdf";
doc1.documentId = "3";
env.documents = [doc1];
email
let signer1 = docusign.Signer.constructFromObject({
email: args.signerEmail,
name: args.signerName,
clientUserId: args.signerClientId,
recipientId: 1,
});
let signHere1 = docusign.SignHere.constructFromObject({
anchorString: "/sn1/",
anchorYOffset: "10",
anchorUnits: "pixels",
anchorXOffset: "20",
});
let signer1Tabs = docusign.Tabs.constructFromObject({
signHereTabs: [signHere1],
});
signer1.tabs = signer1Tabs;
let recipients = docusign.Recipients.constructFromObject({
signers: [signer1],
});
env.recipients = recipients;
env.status = "sent";
return env;
}

Docusign Integration

I can't see Recipients in view(ListStatusChange), just the list of envelopes.
when I write (model.Recipients.Signers.First().Name) returns null
Just I can see model.envelopeId, Status, StatusDateChange. I passed the model IEnumerable (Envelope)
I used templates for send my documents, here is the envelope definition:
public void GenerateDocument(string name, string email)
{
var docuSignClient = new DocuSignClient();
var accountId = docuSignClient.LoginApi(username, password, integratorkey);
var templateRoles = templateRoleNames.Select(m => new TemplateRole
{
Email = email,
Name = name,
RoleName = m
}).ToList();
var envelope = new EnvelopeDefinition
{
EmailSubject = subject,
EmailBlurb = messageBody,
TemplateId = templateId,
TemplateRoles = templateRoles,
Status = "sent"
};
var envelopesApi = new EnvelopesApi();
var envelopesSummary = envelopesApi.CreateEnvelope(accountId, envelope);
You need to include recipients in the options for the ListStatusChanges request.
You didn't show this code, but here is what it may look like:
var apiClient = new ApiClient(basePath);
apiClient.Configuration.DefaultHeader.Add("Authorization", "Bearer " + accessToken);
var envelopesApi = new EnvelopesApi(apiClient);
ListStatusChangesOptions options = new ListStatusChangesOptions { include = "recipients" };
options.fromDate = DateTime.Now.AddDays(-30).ToString("yyyy/MM/dd");
// Call the API method:
EnvelopesInformation results = envelopesApi.ListStatusChanges(accountId, options);
return results;

GMail API Replying to Email Thread Using NodeJS

Dear All: I am sure many of you have discussed above topic multiple times after I am going through all the example and references I have managed to write the code to reply to same email ThreadID. But Unfortunately while I am responding to same ThreadID emails it's going as new email. I have attached my complete NodeJS Code help me to review and let me know where should I have to make the changes.
const {google} = require('googleapis');
const mailComposer = require('nodemailer/lib/mail-composer');
var program_name = process.argv[0]; //value will be "node"
var script_path = process.argv[1]; //value will be "yourscript.js"
var Sender_Email = process.argv[2]; //value will be "Sender Email"
var Receiver_Email = process.argv[3]; //value will be "Email To"
//var CC_Email = process.argv[4]; //value will be "Email Cc"
var Email_Subject = process.argv[4]; //value will be "Email Subject"
var Email_Template = process.argv[5]; //value will be "Email Template"
var ThreadID = process.argv[6]; //Path to attach the file
var Dec_Message_ID = process.argv[7]; //Encoded messageid
var FileAttachment = process.argv[8]; //Path to attach the file
var dateFormat = require('dateformat');
var day=dateFormat(new Date(), "mmm dd, yyyy HH:MM tt");
class CreateMail{
constructor(auth, to, cc, sub, body, task, attachmentSrc = [FileAttachment]){
this.me = Sender_Email;
this.task = task;
this.auth = auth;
this.to = Receiver_Email;
//this.cc = CC_Email;
this.sub = Email_Subject;
var fs = require('fs');
this.body = fs.readFileSync(Email_Template,{encoding:'utf-8'});
this.gmail = google.gmail({version: 'v1', auth});
this.attachment = attachmentSrc;
}
//Creates the mail body and encodes it to base64 format.
makeBody(){
if(this.attachment.length>0){
var arr = [];
for(var i=0;i<this.attachment.length;i++){
arr[i] = {
path: this.attachment[i],
encoding: 'base64'
}
}
}
let mail;
//Mail Body is created.
if(this.attachment.length>0){
mail = new mailComposer({
from: "Arthanareeswaran Chandrasekaran <arthaaadhi#visha.page>",
//sender: this.me,
to: this.to,
//cc: this.cc,
replyTo: this.to,
inReplyTo: "<CAO29sXBTxmE8M=xyTkdFfsrxB_Mdr5e6N6vXiijwTY9rn1kzpQ#mail.gmail.com>",
references: "<CAF7UyHwMrUvy-ZLNyRfjDmX876EKi5T-oc8E_tXy2PwO19dZ_Q#mail.gmail.com> <CAO29sXBH_B0yG4G2p6tdW1uk_tq9qFXmc01CPO5HJopkvMbU4Q#mail.gmail.com> <CAO29sXCcHv4LQSumjht_5zHEYvSzjfYkGr+yCEHfjwnqRvt0=Q#mail.gmail.com> <CAO29sXCPAxzWG0dC-TKEi4cR3xM8hbHhSJQ0ZAhbXBjsp503oA#mail.gmail.com> <CAO29sXA2mpqx6qbEeB5ke_6kUTrwXsqMD8ku0Aq3E_R07YzCLg#mail.gmail.com> <CAO29sXBTxmE8M=xyTkdFfsrxB_Mdr5e6N6vXiijwTY9rn1kzpQ#mail.gmail.com>",
subject: this.sub,
html: this.body,
textEncoding: "base64",
attachments: arr
});
}
else{
mail = new mailComposer({
to: this.to,
cc: this.cc,
html: this.body,
subject: this.sub,
textEncoding: "base64"
});
}
//Compiles and encodes the mail.
mail.compile().build((err, msg) => {
if (err){
return console.log('Error compiling email ' + error);
}
const encodedMessage = Buffer.from(msg)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
if(this.task === 'mail'){
this.sendMail(encodedMessage);
}
else{
this.saveDraft(encodedMessage);
}
});
}
//Send the message to specified receiver.
sendMail(encodedMessage){
this.gmail.users.messages.send({
userId: this.me,
resource: {
raw: encodedMessage,
threadId: ThreadID
}
}, (err, result) => {
if(err){
return console.log('GMail API - The API returned an error: ' + err);
}
console.log("GMail API Sending Email Reply from server:", result.data);
});
}
//Saves the draft.
saveDraft(encodedMessage){
this.gmail.users.drafts.create({
'userId': this.me,
'resource': {
'message': {
'raw': encodedMessage,
threadId: ThreadID
}
}
})
}
//Deletes the draft.
deleteDraft(id){
this.attachment.gmail.users.drafts.delete({
id: id,
userId: this.me
});
}
}
module.exports = CreateMail;
Thanks for your help...
I suggest you update your code to this and check that the References, In-Reply-To and Subjects headers match:
function replyToMessage(auth) {
const gmail = google.gmail({
version: 'v1',
auth
});
const messages = [
'From: NAME <email#email.com>',
'To: NAME <email#email.com>',
'References: <REFERENCE1> <REFERENCE2>',
'In-Reply-To: <IN_REPLY_TO>',
'Content-Type: text/html; charset=utf-8',
'MIME-Version: 1.0',
'Subject: Re: SUBJECT',
'',
'BODY_OF_THE_REPLY',
'',
];
const message = messages.join('\n');
const encodedMessage = Buffer.from(message)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: encodedMessage,
threadId: 'THREAD_ID'
}
});
}
Also please bear in mind that in order to see the replies accordingly, you will have to turn on the Conversation View from Gmail Settings.
According to the documentation:
You can choose whether replies to emails are grouped in conversations, or if each email shows up in your inbox separately.
Hence, if this setting is not turned on, the email will show up separately in your inbox.
Reference
Gmail Help.

How to get user by email?

I am using graph-service to access Microsoft Graph API. Following this document to get user by email. I have write a block of code:
var user = null;
const GraphService = require('graph-service');
const ClientCredentials = require('client-credentials');
const tenant = 'my-company.com';
const clientId = '0b13aa29-ca6b-42e8-a083-89e5bccdf141';
const clientSecret = 'lsl2isRe99Flsj32elwe89234ljhasd8239jsad2sl=';
const credentials = new ClientCredentials(tenant, clientId, clientSecret);
const service = new GraphService(credentials);
service.all('/users').then(response => {
console.log(response.data);
response.data.forEach(function(item) {
if (item.userPrincipalName == "lgomez#my-company.com"){
user = item;
break;
}
});
});
But response.data returns a list of users with cost much resource to get a single user object by email. Please help me to update the code which can return only a single object of user by email.
The operation /users you are using, is for retreiving a list of all users.
You can request as single user by calling:
/users/{id | userPrincipalName}
This operation either accepts an userId or an userPrincipleName
e.g.
/users/foo#bar.com
[more information is in the documentation]
In your case the code could be altered to:
//...
var myUPN = "lgomez#my-company.com"
service.get('/users/' + myUPN).then(response => {
console.log(response.data);
});

mongoose save not working in second part of if statement

I have a function in my Node/Express API where I first check to see if a "Contact" exists in my "Contacts" collection. If it does, I do an update. If one doesn't exist with the email in the query, then I create a new one.
If a contact exists, the contact.save() works. But if the contact doesn't exist, the contact.save() generates an error message saying "contact.save() is not a function.
I don't see what I'm doing wrong. Hoping someone out there can see it easy enough and give me some advice.
Here is the code:
// SEARCH IF CONTACT EXISTS IN SENDERS CONTACTS - BY OWNERID AND EMAIL IN EMAILS
Contact.findOne({'owner_id': req.body.owner_id, 'emails.email_address':req.body.invited_email}, function(err, contact){
if(err)
console.log('Error in looking for contact.');
// IF A CONTACT EXISTS ---
if(contact){
console.log('--> CONTACT FOUND');
// SET CONTACT SHARED INFORMATION
contact.shared.invite_id = req.body.invited_first_name + req.body.invited_last_name + req.body.owner_id;
contact.shared.profile_id = req.body.profile_id;
contact.shared.first_name = req.body.first_name;
contact.shared.company = req.body.company;
contact.shared.title = req.body.title;
contact.shared.last_name = req.body.last_name;
contact.shared.initial = req.body.initial;
contact.shared.linkedin = req.body.linkedin;
contact.shared.google = req.body.google;
contact.shared.facebook = req.body.facebook;
contact.shared.pinterest = req.body.pinterest;
contact.shared.twitter = req.body.twitter;
contact.shared.emails = req.body.emails;
contact.shared.phones = req.body.phones;
contact.shared.addresses = req.body.addresses;
contact.shared.link = req.body.invited_first_name + req.body.invited_last_name + req.body.owner_id;
// ADD TO ALERT INFORMING SENDER THAT HE/SHE SHARED INFO
alerts.genShare(req.body.owner_id, req.body.invited_first_name, req.body.invited_last_name, req.body.invited_email);
// SAVE CONTACT INFORMATION
contact.save(function(err){
if(err)
res.send(err);
res.json(contact);
});
// IF INVITED IS NOT A USER - CREATE USER ACCOUNT
if(!inviteIsUser){
var phones = req.body.phones;
var emails = req.body.emails;
var addresses = req.body.addresses;
var email = [{email_address:req.body.invited_email, email_type:'home'}];
var share = {
invite_id:req.body.invited_first_name + req.body.invited_last_name + req.body.owner_id,
profile_id:req.body.profile_id,
first_name:req.body.first_name,
last_name:req.body.last_name,
initial:req.body.initial,
birthday:req.body.birthday,
highschool:req.body.highschool,
college:req.body.college,
facebook:req.body.facebook,
linkedin:req.body.linkedin,
google:req.body.google,
pinterest:req.body.pinterest,
user_image:req.body.user_image,
emails:emails,
addresses:addresses,
phones:phones,
shared:{}
};
var newContact = {
emails: email,
profile_id: req.body.profile_id,
first_name: req.body.invited_first_name,
last_name : req.body.invited_last_name,
shared:share
};
//CREATE NEW USER AND ADD CONTACT FOR PERSON BEING SHARED WITH
newUser = userCreate.genUser(req.body.invited_email, newContact);
}
} else {
var contact = new Contact();
//IF CONTACT EMAIL IS NOT FOUND, A NEW CONTACT IS CREATED
console.log('Contact NOT found');
var phones = req.body.phones;
var emails = req.body.emails;
var addresses = req.body.addresses;
var email = [{email_address:req.body.invited_email, email_type:'home'}];
var share = {
invite_id:req.body.invited_first_name + req.body.invited_last_name + req.body.owner_id,
profile_id:req.body.profile_id,
first_name:req.body.first_name,
last_name:req.body.last_name,
initial:req.body.initial,
birthday:req.body.birthday,
highschool:req.body.highschool,
college:req.body.college,
facebook:req.body.facebook,
linkedin:req.body.linkedin,
google:req.body.google,
pinterest:req.body.pinterest,
user_image:req.body.user_image,
emails:emails,
addresses:addresses,
phones:phones,
link:req.body.invited_first_name + req.body.invited_last_name + req.body.owner_id,
shared:{}
};
contact = {
emails: email,
owner_id:req.body.owner_id,
profile_id:req.body.profile_id,
first_name: req.body.invited_first_name,
last_name : req.body.invited_last_name,
shared:share
};
console.log('Contact: ', contact);
// SAVE CONTACT INFORMATION
contact.save(function(err){
if(err)
res.send(err);
res.json(contact);
});
// // IF INVITED IS NOT A USER - CREATE USER ACCOUNT
// if(!inviteIsUser){
// newUser = userCreate.genUser(req.body.invited_email, contact);
// }
// ADD TO ALERT INFORMING SENDER THAT HE/SHE SHARED INFO
alerts.genShare(req.body.owner_id, req.body.invited_first_name, req.body.invited_last_name, req.body.invited_email);
}
})
You have rewritten your contact with an object:
contact = {
emails: email,
owner_id:req.body.owner_id,
profile_id:req.body.profile_id,
first_name: req.body.invited_first_name,
last_name : req.body.invited_last_name,
shared:share
};
And then you try to call function save which is not present there.

Resources