Send a document from Laravel with JWT Grant authentication

In this blog post, I will demonstrate how to incorporate DocuSign into Laravel while authenticating via OAuth JWT Grant. Integrating eSign is simple thanks to the DocuSign eSign PHP Laravel wrapper.

Requirements

  • PHP 7.4+
  • Developer account
  • Working Laravel application and installed Composer (The guide is using Ubuntu 18.04, Laravel 9.x, and PHP 7.4.)

Installation

DocuSign has an official Laravel wrapper available on GitHub. The benefit it provides is that it will fetch the latest eSignature PHP SDK client package for you from the package manager, so you will not need to pull it separately. Moreover, this is an official provider by DocuSign, so we can provide required support if needed.

To install the wrapper in your Laravel application, follow the steps in the README documentation.

Installing through Composer:

composer require docusign/esign-laravel

For elevated privileges on Linux, you may need to add sudo.

After a successful installation, you should see a new docusign directory within your app root vendor directory.

Add the service provider to the provider's array in config/app.php:

DocuSign\eSign\ESignServiceProvider::class

Use case

This example implements the following functions:

  • Allow the user to upload the document and provide the recipient's name and email address.
  • Create an envelope with a composite template.
  • Send the envelope to be signed remotely.

Note: Please keep in mind that the code in this article is intended for a document containing a SignHere tab. The tab is placed via the anchor string "**signature**" as referenced in the code. You need to insert this string into your document at any place where you want the recipient to sign.

Configuration

Connecting to DocuSign from Laravel requires some configuration information, which you can store in the Laravel .env file. Add the following lines at the end of your .env file:

DS_CLIENT_ID="xxxxxx-xxxx-xxxx-xxxxxxx"
DS_IMPERSONATED_USER_ID="xxxxxx-xxxx-xxxx-xxxxxxx"
DS_JWT_SCOPE="signature impersonation"
DS_AUTH_SERVER="account-d.docusign.com"
DS_ESIGN_URI_SUFFIX="/restapi"
DS_KEY_PATH="jwt/private.key"

Notes:

  • DS_CLIENT_ID: Your application Integration key
  • DS_IMPERSONATED_USER_ID: The User ID of the user to be impersonated
  • DS_DEMO_AUTH_SERVER: The account server we request the token from. (To request token in production replace it with - account.docusign.com)

You can read more about these values in our JWT guide, How to get an access token with JWT Grant authentication.

Another important step before generating an access token is to include your private key in Laravel. If you have not yet generated your private key, go to the RSA key pair section of the Configure your App tutorial. To add the private key, create a new directory named jwt under storage and a new file called private.key. Copy and paste your private key without any modifications.

Controller

Once you’ve completed the configuration steps, you can move on to create your Controller. The controller name is totally up to you, but keep in mind to include “Controller” in the name; for instance:   

php artisan make:controller ContractController

Thanks to Artisan (a command line interface included with Laravel), the command will generate a new controller class for you under the app/Http/Controllers directory.

Routes

To have access to your controller, you need to update the routes/web.php file by adding these two lines:

Route::get('/contract',[ContractController::class,'index']);
Route::post('/contract',[ContractController::class,'send']);

Views

Views for your controller will be required to gather or show information from/to the user. To separate your views from the rest, create a subdirectory named contract under resources/views. Create two view files for this use case: create.blade.php and response.blade.php, respectively. The “create” view will be used to display the form, and the “response” view will be used to display the outcome of your envelope creation call.

The ContractController logic is separated into four functions.

Here’s how each of those functions contributes to the solution.

Function index

The index function returns the “create” view that contains the form.

Function send

The send function is the core function, containing the main logic. That logic proceeds through four steps:

Step 1 

In this step, the function does form validation using Laravel’s built in Validator facade:

$validator = Validator::make($request->all(), [
    'formFile' => 'required|mimes:doc,docx,pdf|max:2048',
    'name' => 'required|min:3|max:50',
    'email' => 'required|email|max:255'
]);        
if ($validator->fails()) {
    return redirect('/contract')
    ->withErrors($validator)
    ->withInput();
} 

Step 2

Here, the function instantiates the eSignature API client, sets the correct authentication path, and requests a new token by calling the helper getToken function:

$apiClient = new ApiClient();
$apiClient->getOAuth()->setOAuthBasePath(env('DS_AUTH_SERVER')); 
try{
    $accessToken = $this->getToken($apiClient);
} catch (\Throwable $th) {
    return back()->withError($th->getMessage())->withInput();
}

Step 3

In this step, the function requests the authenticated user info. This is one of the most underappreciated API requests; the reason being is that it returns the user account info together with the base_path (including the site where the accounts are located, such as demo, eu, or au).

$userInfo = $apiClient->getUserInfo($accessToken);
$accountInfo = $userInfo[0]->getAccounts();

Once the function receives the response, it updates the API client base path:

$apiClient->getConfig()->setHost($accountInfo[0]->getBaseUri() . env('DS_ESIGN_URI_SUFFIX'));

Step 4

Here, the function builds the envelope body by using the helper function buildEnvelope:

$envelopeDefenition = $this->buildEnvelope($request);

Then, it makes an API createEnvelope request to DocuSign to create the envelope:

try {            
     $envelopeApi = new EnvelopesApi($apiClient);
     $result = $envelopeApi->createEnvelope($accountInfo[0]->getAccountId(), $envelopeDefenition);
     } catch (\Throwable $th) {
        return back()->withError($th->getMessage())->withInput();
}      

Function buildEnvelope

The buildEnvelope function is just a helper function I’ve put in place to separate the envelope build logic.

Function getToken

The getToken function reads the content of the private key and makes an API request call to the DocuSign account server using the built-in requestJWTUserToken function:

private function getToken(ApiClient $apiClient) : string{
        try {
            $privateKey = file_get_contents(storage_path(env('DS_KEY_PATH')),true);
            $response = $apiClient->requestJWTUserToken(
                $ikey = env('DS_CLIENT_ID'),
                $userId = env('DS_IMPERSONATED_USER_ID'),
                $key = $privateKey,
                $scope = env('DS_JWT_SCOPE')
            );        
            $token = $response[0];
            $accessToken = $token->getAccessToken();
        } catch (\Throwable $th) {
            throw $th;
        }    
        return $accessToken;        
}

You can find the full code for this project on GitHub.

As you can see from this example, it is very easy to set up and use JWT to authenticate and send documents thanks to DocuSign’s PHP SDK client.

Additional resources

Ivan DInkov
Author
Ivan Dinkov
Sr. Developer Support Advisory Engineer
Published