I have just started reading up docusign and have to implement it in my project using PHP. The requirement being, once user accepts the offer, he is directed to the document for signing. I understood the template and envelop creation but am stuck at the first step of authorization. I used the Legacy Header Authentication which is easy and works. But they are discouraging using this method anymore. So what to do instead of this?
Thanks in advance.
Is your application used to send out the request for signing?
If so, then the user of your application should probably have their own account on DocuSign. You should use OAuth authorization code grant to let your app's user login and send out the signing request.
For example, an employee uses your app to send out offer letters. In this case, your employee would authenticate himself to DocuSign via your app using OAuth Authorization Code Grant.
Or is the user of your application the signer who will be agreeing to something via DocuSign? If so then your app needs to create an envelope to be signed by the signer. Since the user of your application in this case is not a member of your company/organization, you need your app to impersonate someone who is a member of your org.
In this case, your app can use JWT authentication with impersonation to act on behalf of someone.
For example, your application is used by potential new employees to agree to the offered employment contract. In this case, the user of your app (the new employee) doesn't have a DocuSign login. So your app impersonates (using the JWT flow) an HR person in your company. Your app then, on behalf of the HR person, enables the new employee to sign the offer letter or generate new letter that will be sent for signing via DocuSign.
If JWT authentication fits your user case, we have a code example for PHP. See https://github.com/docusign/eg-01-php-jwt
We also have an Authorization code grant example for PHP.
I tried "rolling my own" JWT authentication, but gave up. I have found that Chilkat (chilkatsoft.com) works well:
Function Authenticate(SenderEmail2 As String) As Boolean
'MsgBox("AuthToDocuSign.Authenticate()") 'IHF 04/28/22
Authenticate = False
Dim oauth2 As New Chilkat.OAuth2
Dim success As Boolean
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12 'IHF 05/01/22
' This should be the port in the localhost Redirect URI for your app defined in the Docusign developer portal.
' The Redirect URI would look like "http://localhost:3017/" if the port number is 3017.
oauth2.ListenPort = 8080
' For developer sandbox environment, authorization endpoint is https://account-d.docusign.com/oauth/auth
' For production platform, authorization endpoint is https://account.docusign.com/oauth/auth
oauth2.AuthorizationEndpoint = "https://account.docusign.com/oauth/auth"
oauth2.TokenEndpoint = "https://account.docusign.com/oauth/token"
oauth2.ClientId = "c55048e7-fae1-4ad1-b223-258fce040f57" 'PROD. Also known as Integration Key
' This is your secret key for the authorization code grant.
oauth2.ClientSecret = "f1ddad37-a731-44b1-9679-e7f4268ec4a2" 'PROD. Also known as Secret Key [Fix 04/28/22] ?
oauth2.Scope = "signature"
'oauth2.Scope = "signature impersonation" 'IHF 02/14/22
oauth2.RedirectAllowHtml = "<html><head><meta http-equiv='refresh' content='0;url=https://app.docusign.com'></head><body>Thank you for allowing access.</body></html>" 'PROD. appdemo.docusign.com in DEV
' Begin the OAuth2 three-legged flow. This returns a URL that should be loaded in a browser.
Dim url As String = oauth2.StartAuth()
If (oauth2.LastMethodSuccess <> True) Then
Debug.WriteLine(oauth2.LastErrorText)
Exit Function
End If
ServicePointManager.Expect100Continue = True 'IHF 02/28/22
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 'IHF 02/28/22
Process.Start("C:\Program Files\Internet Explorer\iexplore.exe", url)
' Now wait for the authorization.
' We'll wait for a max of 30 seconds.
Dim numMsWaited As Integer = 0
While (numMsWaited < 30000) And (oauth2.AuthFlowState < 3)
oauth2.SleepMs(100)
numMsWaited = numMsWaited + 100
End While
' If there was no response from the browser within 30 seconds, then
' the AuthFlowState will be equal to 1 or 2.
' 1: Waiting for Redirect. The OAuth2 background thread is waiting to receive the redirect HTTP request from the browser.
' 2: Waiting for Final Response. The OAuth2 background thread is waiting for the final access token response.
' In that case, cancel the background task started in the call to StartAuth.
If (oauth2.AuthFlowState < 3) Then
oauth2.Cancel()
Debug.WriteLine("No response from the browser!")
Exit Function
End If
' Check the AuthFlowState to see if authorization was granted, denied, or if some error occurred
' The possible AuthFlowState values are: 3: Completed with Success. 4: Completed with Access Denied. 5: Failed Prior to Completion.
If (oauth2.AuthFlowState = 5) Then
Debug.WriteLine("OAuth2 failed to complete.")
Debug.WriteLine(oauth2.FailureInfo)
Exit Function
End If
If (oauth2.AuthFlowState = 4) Then
Debug.WriteLine("OAuth2 authorization was denied.")
Debug.WriteLine(oauth2.AccessTokenResponse)
Exit Function
End If
If (oauth2.AuthFlowState <> 3) Then
Debug.WriteLine("Unexpected AuthFlowState:" & oauth2.AuthFlowState)
Exit Function
End If
Debug.WriteLine("OAuth2 authorization granted!")
Debug.WriteLine("Access Token = " & oauth2.AccessToken)
accessToken = oauth2.AccessToken 'IHF 02/14/22
' Get the full JSON response:
Dim json As New Chilkat.JsonObject
json.Load(oauth2.AccessTokenResponse)
json.EmitCompact = False
Debug.WriteLine(json.Emit())
' Save the JSON to a file for future requests.
Dim fac As New Chilkat.FileAccess
fac.WriteEntireTextFile("qa_data/tokens/docusign.json", json.Emit(), "utf-8", False)
Authenticate = success
End Function 'IHF 04/28/22
Related
Hi I have google spreadsheet that contains customers' ID and their shipping status. I want to create google form where customers are able to input each of their own ID, with the return that the google form shows their shipping status.
I tried to look for solutions in internet but there was no luck. I am not really good in programming, i hope there is answer to this problem without having me to do some hard programming.
The sample case can be seen in here: https://docs.google.com/spreadsheets/d/14vSAeZxEJTzbNLLYEiref6qt-CMqiVi8alheLcIBugM/edit?usp=sharing
Google form should show something a little bit like is shown in cell D1:E3.
Where customers can fill the form with their own customer id, and the google form shows the status.
Consideration
There is no way to respond back in a Google Form directly. You can't show custom validation messages after From submission either.
Proposed solution
What about using email addresses additionally to the customerID to retrieve the shipping status? In this way you can easily build a notification system that will send an email if a matching customer ID is found in your spreadsheet.
To build such system you will have to build a Form with a Trigger.
It is required a bit of programming but I will try to cover the most important parts the best I can:
Adapt your Database structure
Add the customers email addresses in the column C in order to be able to retrieve it using the same customer ID.
| A | B | C |
|----+--------+-------|
| ID | STATUS | EMAIL |
Build the Form Trigger
In the Form you are using click on the 3 dots from the top menu and select Script Editor. Here you will write the code that will power your notification system.
function installTrigger() {
// This function instructs the program to trigger the checkID function whenever a form response is submitted
ScriptApp.newTrigger('checkID')
.forForm(FormApp.getActiveForm())
.onFormSubmit()
.create();
}
function checkID(e) {
// This function will parse the response to use the customer ID to retrieve email address and shipping status from the Spreadsheet Database
var responses = e.response.getItemResponses(); // Gets the form responses
var id = responses[0].getResponse(); // Assuming the first answer (index 0) is the customer ID)
var found = SpreadsheetApp.openById('spreadsheet_id')
.getRange('Sheet1!A1:C8') // The spreadsheet cells range in A1 Notation
.getValues() // Retrieve their values in rows
.filter((row) => row[0] == id); // Filter the rows for the provided customer ID
if (found) {
var status = found[0][1]; //Column B
var email = found[0][2]; //Column C
var subject = "Shipping Status";
var message =
`Hello!
The status of the order number ${id} is: ${status}.`
MailApp.sendEmail(email, subject, message);
}
}
Install the trigger
From the Script Editor top menu run the installTrigger() function: Run>Run function>installTrigger.
You are done
Following these steps you have successfully set up the notification system. Now you can start sharing the Form link and accept responses.
References
Installable Triggers
Mail App
I'm trying to enable Lockout in our app.
In IdentityConfig.cs is the code to set some of the necessary flags.
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
I set 'ShouldLockout' to true in the AccountController's "Login" function.
But where does one set the last bit, i.e.
manager.SetLockoutEndDate(UserId, manager.DateTime.UtcNow.AddYears(50));
The setLockoutEndDate, needs the userId. Is that done at registration?
Thanks
I tried setting it at RegisterUser and that works.
I have a SAML implementation which works fine with JumpCloud, OneLogin and other providers, however integration with Microsoft Azure AD is proving difficult. We received the error
AADSTS75005: The request is not a valid Saml2 protocol message.
Whenever we send our requests over. I have tried the solutions mentioned here and here but neither fixes the issue for us.
My code to create the SAML Request, which opens in a new window via some Javascript is:
Using sw As StringWriter = New StringWriter()
Dim xws As XmlWriterSettings = New XmlWriterSettings()
xws.OmitXmlDeclaration = True
Dim assertionUrl As String = "OUR URL"
Dim issuer As String = "OUR ISSUER TEXT"
dim id as string = "_" + System.Guid.NewGuid().ToString()
dim issue_instant as string = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
Using xw As XmlWriter = XmlWriter.Create(sw, xws)
xw.WriteStartElement("samlp", "AuthnRequest", "urn:oasis:names:tc:SAML:2.0:protocol")
xw.WriteAttributeString("ID", id)
xw.WriteAttributeString("Version", "2.0")
xw.WriteAttributeString("IssueInstant", issue_instant)
xw.WriteAttributeString("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
xw.WriteAttributeString("AssertionConsumerServiceURL", assertionUrl)
xw.WriteStartElement("saml", "Issuer", "urn:oasis:names:tc:SAML:2.0:assertion")
xw.WriteString(issuer)
xw.WriteEndElement()
xw.WriteStartElement("samlp", "NameIDPolicy", "urn:oasis:names:tc:SAML:2.0:protocol")
xw.WriteAttributeString("Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified")
xw.WriteAttributeString("AllowCreate", "true")
xw.WriteEndElement()
xw.WriteStartElement("samlp", "RequestedAuthnContext", "urn:oasis:names:tc:SAML:2.0:protocol")
xw.WriteAttributeString("Comparison", "exact")
xw.WriteStartElement("saml", "AuthnContextClassRef", "urn:oasis:names:tc:SAML:2.0:assertion")
xw.WriteString("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")
xw.WriteEndElement()
xw.WriteEndElement() ' RequestedAuthnContext
xw.WriteEndElement()
End Using
If (format = AuthRequestFormat.Base64)
Dim toEncodeAsBytes As Byte() = System.Text.ASCIIEncoding.ASCII.GetBytes(sw.ToString())
Return System.Convert.ToBase64String(toEncodeAsBytes)
End If
Return Nothing
End Using
We then redirect the user to: https://login.microsoftonline.com/OUR_APP_ID/saml2?SAMLRequest= followed by the encoded string
I have base64decoded my result, and read it as a raw string and the XML appears valid to me. I have even gone so far as to take the example Request from Azure AD and hard-coded my implementation to that, with replaced issuer/instant/id etc. This leads me to believe it is an encoding issue in my request. However, the changed encoding in Dushyant Gill's answer does not resolve my problem either.
I've found various other forum posts where other software vendors have mentioned that they have had to apply changes to their SAML implementations when their customers have complained that it didn't work with Azure - however I've yet to find what their resolution was.
If you are using HTTP redirect binding (which is suggested by
We then redirect the user to
) , then the SAMLRequest has to be encoded using DEFLATE encoding. Please see section 3.4.4 of http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf
I have been trying to set up an ExpressCheckout with recurring payments but I don't find the solution.
Having a look at the documentation (Recurring Payments With the Express Checkout API), the diagram gives a sequence where "CreateRecurringPaymentsProfile" is invoked at the end.
Now, having a look at the other documentation (How to Create a Recurring Payments Profile with Express Checkout), the different steps which are explained give a different sequence order, where "CreateRecurringPaymentsProfile" comes directly after "GetExpressCheckoutDetails".
I tried to follow this second example but I systematically receive an error.
Could someone tell me what I exactly need to do?
Of course a practical example would be more than welcome...
In advance, many thanks
Additional information:
The error I am receiving is "INVALID TOKEN".
Here is the information I send:
VERSION=84.0
METHOD=CreateRecurringPaymentsProfile
LOCALECODE=FR
TOKEN=[the one I received from SetExpressCheckout]
PROFILESTARTDATE=[the date of the next payment]
BILLINGPERIOD=Month
BILLINGFREQUENCY=6
TOTALBILLINGCYCLES=0
AMT=[the same as I mentioned in PAYMENTREQUEST_0_AMT]
AUTOBILLAMT=AddToNextBilling
CURRENCYCODE=EUR
MAXFAILEDPAYMENTS=3
DESC=[the same as I mentioned in L_BILLINGAGREEMENTDESCRIPTION0]
L_PAYMENTREQUEST_0_NAME0=[the same as I used in SetExpressCheckout]
L_PAYMENTREQUEST_0_DESC0=[the same as I used in SetExpressCheckout]
L_PAYMENTREQUEST_0_AMT0=[the same as I used in SetExpressCheckout]
L_PAYMENTREQUEST_0_QTY0=[the same as I used in SetExpressCheckout]
L_PAYMENTREQUEST_0_TAXAMT0=[the same as I used in SetExpressCheckout]
Do I also need to mention:
L_BILLINGAGREEMENTDESCRIPTION0 & L_BILLINGTYPE0 ?
In case you get 11502 Invalid token, you will need to pass the following variables in your SetEC API request:
BILLINGAGREEMENTDESCRIPTION=Your billing agreement name
BILLINGTYPE=RecurringPayments
Please, check below how EC+RP flow works:
1) SetExpressChekout + BILLINGAGREEMENTDESCRIPTION and BILLINGTYPE variables
VERSION = 86.0
METHOD = SetExpressCheckout
RETURNURL =
http://www.website.com/return.php
CANCELURL =
http://www.website.com/cancel.php
PAYMENTREQUEST_0_CURRENCYCODE =
USD
PAYMENTREQUEST_0_PAYMENTACTION = SALE
L_BILLINGTYPE0 =
RecurringPayments
L_BILLINGAGREEMENTDESCRIPTION0 = SameEveryTime
PAYMENTREQUEST_0_AMT = 1.00
PAYMENTREQUEST_0_ITEMAMT = 1.00
PAYMENTREQUEST_0_DESC = Labs
L_PAYMENTREQUEST_0_NAME0 = Lab 1
L_PAYMENTREQUEST_0_NUMBER0 = 10101
L_PAYMENTREQUEST_0_QTY0 = 1
L_PAYMENTREQUEST_0_AMT0 = 1.00
L_PAYMENTREQUEST_0_DESC0 = Download
2) Login into your PayPal account and click on “Accept and Continue”
https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-2EJ022116H3067544
Buyer logs into his PayPal account and click on the button “Agree and Continue”,
then he will be redirect to RETURNURL+ token+ PayerID:
http://www.website.com/return.php?token=EC-2EJ022116H3067544&PayerID=HHHAPCFUM9ULW
3) Then you can run CreateRecurringPaymentsProfile + token:
PROFILEREFERENCE=RPInvoice123
PROFILESTARTDATE=2016-08-17T14:30:00Z
SUBSCRIBERNAME=Mr Sub Scriber
METHOD=CreateRecurringPaymentsProfile
TOKEN=XXXXXXXXXXXXX
DESC=SameEveryTime
AMT = 1.00
BILLINGPERIOD=Day
BILLINGFREQUENCY=3
VERSION=86.0
MAXFAILEDPAYMENTS=1
L_PAYMENTREQUEST_0_AMT0=1.00
L_PAYMENTREQUEST_0_NAME0=Lab 1
L_PAYMENTREQUEST_0_NUMBER0=10101
L_PAYMENTREQUEST_0_QTY0=1
L_BILLINGTYPE0=RecurringPayments
L_BILLINGAGREEMENTDESCRIPTION0=SameEveryTime
L_PAYMENTREQUEST_0_ITEMCATEGORY0=Digital
and this is the NVP Response:
PROFILEID=I-TOKEN123456
PROFILESTATUS=ActiveProfile
TIMESTAMP=2013-11-22T04:06:50Z
CORRELATIONID=2b5be15a871ff
ACK=Success
VERSION=86.0
BUILD=5908853
Documentation:
ECRecurringPayments
API references (NVP)
SetExpressCheckout API request
CreateRecurringPayment API request
I am creating a web application with Ruby on Rails 3.1 (RC1). I am using Factory Girl, RSpec and Cucumber (with Capybara) for testing, but I am experiencing unexpected raised ActionDispatch::ClosedErrors some of the times (not every time) when I am creating new users (through the User model's create action). Below is the error message that I get:
Cannot modify cookies because it was closed. This means it was already streamed
back to the client or converted to HTTP headers. (ActionDispatch::ClosedError)
The error is raised when using these ways of creating users:
Creation using Factory Girl
Factory.create( :user )
Factory.build( :user ).save
Basic creation
User.create( { ... } )
User.new( { ... } ).save
What is funny is that they do work during some test, but not in others, and it does not seem random, although I cannot figure out the reason. Below is an excerpt from my code:
users_controller_spec.rb
require 'spec_helper'
def user
#user ||= Factory.create( :user )
end
def valid_attributes
Factory.attributes_for :user
end
describe UsersController do
describe 'GET index' do
it 'assigns all users as #users' do
users = [ user ] # The call to user() raises the error here
get :index
assigns[ :users ].should == users
end
end
describe 'GET show' do
it 'assigns the requested user as #user' do
get :show, id: user.id # The call to user() raises the error here
assigns[ :user ].should == user
end
end
However, the error is not raised in the following code block:
describe 'GET edit' do
it 'assigns the requested user as #user' do
get :edit, id: user.id # This raises no error
assigns[ :user ].should == user
end
end
Any other method below this does not raise the error, even though I am creating users in the exact same way.
Any suggestions to what I might be doing wrong would be greatly appreciated!
Someone posted a workaround here
https://github.com/binarylogic/authlogic/issues/262#issuecomment-1804988
This is due to the way rails 3 streams the response now. They posted a fix in edge for the same issue in flash but not in cookies yet. For now I have turned off my request specs. I am going to look at the problem this weekend if no one gets to it before then.
https://github.com/rails/rails/issues/1452
Just so we don't have to follow links, here's my modified version of the authlogic workaround:
class User < ActiveRecord::Base
acts_as_authentic do |c|
c.maintain_sessions = false if Rails.env == "test"
end
end
Rather than deal with ensuring session management on every .save call, I just turn them off if I'm testing.