Skip to content

Commit 180b3e0

Browse files
committed
Adding support for Authorization Code Grant in addition to JWT
1 parent ff976d3 commit 180b3e0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1115
-80
lines changed

.env

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Configuration file for the example
22
##
3-
# Integrator Key is the same as client id
3+
# Integration Key is the same as client id
44
DS_CLIENT_ID={DS_CLIENT_ID}
5+
# Integration secret key
6+
DS_CLIENT_SECRET={DS_CLIENT_SECRET}
57

68
# API Username
79
DS_IMPERSONATED_USER_GUID={DS_USER_GUID}
@@ -18,12 +20,12 @@ DS_PAYMENT_GATEWAY_DISPLAY_NAME={DS_PAYMENT_GATEWAY_DISPLAY_NAME}
1820
# NOTE: the Python config file parser requires that you
1921
# add a space at the beginning of the second and
2022
# subsequent lines of the multiline key value:
21-
DS_PRIVATE_KEY={RSA_KEY}
23+
DS_PRIVATE_KEY={DS_PRIVATE_KEY}
2224

2325
# React environment variables
2426
#UI and BE links
25-
REACT_APP_DS_RETURN_URL=http://localhost:3000
26-
REACT_APP_API_BASE_URL=http://localhost:5001/api
27+
REACT_APP_DS_RETURN_URL={REACT_APP_DS_RETURN_URL}
28+
REACT_APP_API_BASE_URL={REACT_APP_API_BASE_URL}
2729

2830
# The DS Authentication server
2931
REACT_APP_DS_AUTH_SERVER=https://account-d.docusign.com

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
*.pyc
22
.idea
3-
.env
43

54
# pyenv
65
.python-version
@@ -11,4 +10,4 @@
1110

1211
# frontend
1312
/node_modules
14-
/build
13+
/build

README.MD

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Python and React: MySure Insurance Sample Application
22

3-
### GitHub repo: sample-app-mysure-python
4-
Visit [MySure sample application](https://mysuresample.esigndemos.com/) on DoucSign to see this code publicly hosted
3+
### Github repo: MySureSampleApp
54

65
## Introduction
76
MySuresample application written in Python 3.7 (server) and React (client). You can find a live instance running at [https://mysuresample.esigndemos.com/](https://mysuresample.esigndemos.com/).
@@ -30,7 +29,6 @@ MySure demonstrates the following:
3029
## Installation
3130

3231
### Prerequisites
33-
3432
* A DocuSign Developer Sandbox account (email and password) on [demo.docusign.net](https://demo.docusign.net). If you don't already have a developer sandbox, create a [free account](https://go.docusign.com/sandbox/productshot/?elqCampaignId=16535).
3533
* A DocuSign integration key (a client ID) that is configured to use **JSON Web Token (JWT) Grant**.
3634
You will need the **integration key** itself and its **RSA key pair**. To use this application, you must add your application's **Redirect URI** to your integration key. This [**video**](https://www.youtube.com/watch?v=GgDqa7-L0yo) demonstrates how to create an integration key (client ID) for a user application like this example.
@@ -41,6 +39,7 @@ MySure demonstrates the following:
4139
### Required environment variables
4240

4341
* **DS_CLIENT_ID** - The integration key is the same as the client ID
42+
* **DS_CLIENT_SECRET** - Integration Secret Key
4443
* **DS_IMPERSONATED_USER_GUID** - API account ID
4544
* **DS_TARGET_ACCOUNT_ID** - Target account ID. Use FALSE to indicate the user's default
4645
* **DS_PAYMENT_GATEWAY_ID** - Payment gateway ID (Only Stripe method supported)

app/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from flask_cors import CORS
33
from flask_session import Session
44

5-
from app.api import clickwrap, requests, common
5+
from app.api import clickwrap, requests, common, auth
66

77

88
URL_PREFIX = '/api'
@@ -12,5 +12,6 @@
1212
app.register_blueprint(clickwrap, url_prefix=URL_PREFIX)
1313
app.register_blueprint(common, url_prefix=URL_PREFIX)
1414
app.register_blueprint(requests, url_prefix=URL_PREFIX)
15+
app.register_blueprint(auth, url_prefix=URL_PREFIX)
1516
Session(app)
16-
cors = CORS(app)
17+
cors = CORS(app)

app/api/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from app.api.clickwrap import clickwrap
22
from app.api.common import common
33
from app.api.requests import requests
4+
from app.api.auth import auth

app/api/auth.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from docusign_esign import ApiException
2+
from flask import Blueprint, jsonify, request, redirect, url_for
3+
from flask_cors import cross_origin
4+
5+
from app.api.utils import process_error
6+
from app.ds_client import DsClient
7+
8+
auth = Blueprint('auth', __name__)
9+
10+
11+
@auth.route('/code_grant_auth', methods=['GET'])
12+
@cross_origin(support_creadentials=True)
13+
def code_grant_auth():
14+
try:
15+
url = DsClient.code_auth()
16+
except ApiException as ex:
17+
return process_error(ex)
18+
return jsonify({
19+
'reason': 'Unauthorized',
20+
'response': 'Permissions should be granted for current integration',
21+
'url': url}), 401
22+
23+
24+
@auth.route('/callback', methods=['POST'])
25+
@cross_origin(support_creadentials=True)
26+
def callback():
27+
try:
28+
try:
29+
req_json = request.get_json(force=True)
30+
code = req_json['code']
31+
except TypeError:
32+
return jsonify(message='Invalid json input'), 400
33+
DsClient.callback(code)
34+
except ApiException:
35+
return redirect(url_for("auth.jwt_auth"), code=307)
36+
return jsonify(message="Logged in with code grant"), 200
37+
38+
39+
@auth.route('/jwt_auth', methods=['POST'])
40+
@cross_origin(support_creadentials=True)
41+
def jwt_auth():
42+
try:
43+
DsClient.update_token()
44+
except ApiException as ex:
45+
return process_error(ex)
46+
return jsonify(message="Logged in with JWT"), 200
47+
48+
49+
@auth.route('/get_status', methods=['GET'])
50+
@cross_origin(support_creadentials=True)
51+
def get_status():
52+
if DsClient.code_grant:
53+
return jsonify(logged=DsClient.logged, auth_type="code_grant"), 200
54+
elif DsClient.jwt_auth:
55+
return jsonify(logged=DsClient.logged, auth_type="jwt"), 200
56+
return jsonify(logged=DsClient.logged, auth_type="undefined"), 200
57+
58+
59+
@auth.route('/logout', methods=['POST'])
60+
@cross_origin(support_credentials=True)
61+
def log_out():
62+
DsClient.destroy()
63+
return jsonify(message="Logged out"), 200
64+
65+
66+
@auth.route('/check_payment', methods=['GET'])
67+
@cross_origin(support_credentials=True)
68+
def check_payment():
69+
try:
70+
if DsClient.check_payment_gateway():
71+
return jsonify(message="User has a payment gateway account"), 200
72+
else:
73+
return jsonify(message="User doesn't have a payment gateway account"), 402
74+
except ApiException as ex:
75+
return process_error(ex)

app/api/clickwrap.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
from flask import Blueprint, jsonify, request
33
from flask_cors import cross_origin
44

5-
from app.api.utils import process_error
5+
from app.api.utils import process_error, check_token
66
from app.clickwrap import Clickwrap
77

88
clickwrap = Blueprint('clickwrap', __name__)
99

1010

1111
@clickwrap.route('/clickwraps/renewal', methods=['POST'])
1212
@cross_origin(supports_credentials=True)
13+
@check_token
1314
def insurance_renewal():
1415
"""Create a clickwrap for submitting insurance policy renewal"""
1516
try:

app/api/envelope.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from docusign_esign import ApiException
2+
from flask import request, jsonify
3+
4+
from app.api import authenticate, api
5+
from app.envelope import Envelope
6+
7+
8+
@api.route('/envelope/multi_signers', methods=['POST'])
9+
@authenticate
10+
def multi_signers():
11+
try:
12+
req_json = request.get_json(force=True)
13+
student = req_json['student']
14+
envelope_args = {
15+
"signer_client_id": 1000,
16+
"ds_return_url": req_json['callback-url'],
17+
"account_id": req_json['account-id'],
18+
"base_path": req_json['base-path'],
19+
'access_token': request.headers.get('Authorization').replace('Bearer ', '')
20+
}
21+
except TypeError:
22+
return jsonify(message="Invalid json input"), 400
23+
24+
try:
25+
envelope = Envelope.create('multi-sign.html', student, envelope_args)
26+
envelope_id = Envelope.send(envelope, envelope_args)
27+
result = Envelope.get_view(envelope_id, envelope_args, student)
28+
except ApiException as e:
29+
return jsonify({'error': e.body.decode('utf-8')}), 400
30+
return jsonify({"envelope_id": envelope_id, "redirect_url": result.url})
31+
32+
33+
@api.route('/envelope/list', methods=['POST'])
34+
@authenticate
35+
def envelope_list():
36+
try:
37+
req_json = request.get_json(force=True)
38+
envelope_args = {
39+
'account_id': req_json['account-id'],
40+
'from_date': req_json['from-date'],
41+
"base_path": req_json['base-path'],
42+
'access_token': request.headers.get('Authorization').replace('Bearer ', '')
43+
}
44+
except TypeError:
45+
return jsonify(message="Invalid json input"), 400
46+
47+
try:
48+
envelopes_information = Envelope.list(envelope_args)
49+
except ApiException as e:
50+
return jsonify({'error': e.body.decode('utf-8')}), 400
51+
return jsonify({"envelopes": envelopes_information.to_dict()})

app/api/requests.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from flask import Blueprint, jsonify, request, session
33
from flask_cors import cross_origin
44

5-
from app.api.utils import process_error
5+
from app.api.utils import process_error, check_token
66
from app.document import DsDocument
77
from app.ds_config import DsConfig
88
from app.envelope import Envelope
@@ -12,6 +12,7 @@
1212

1313
@requests.route('/requests/claim', methods=['POST'])
1414
@cross_origin(supports_credentials=True)
15+
@check_token
1516
def submit_claim():
1617
"""Submit a claim"""
1718
try:
@@ -45,12 +46,14 @@ def submit_claim():
4546

4647
@requests.route('/requests/newinsurance', methods=['POST'])
4748
@cross_origin(supports_credentials=True)
49+
@check_token
4850
def buy_new_insurance():
4951
"""Request for the purchase of a new insurance"""
5052
try:
5153
req_json = request.get_json(force=True)
5254
insurance_info = req_json['insurance']
5355
user = req_json['user']
56+
5457
envelope_args = {
5558
'signer_client_id': 1000,
5659
'ds_return_url': req_json['callback-url'],
@@ -104,6 +107,7 @@ def envelope_list():
104107

105108
@requests.route('/requests/download', methods=['GET'])
106109
@cross_origin(supports_credentials=True)
110+
@check_token
107111
def envelope_download():
108112
"""Request for document download from the envelope"""
109113
try:

app/api/user.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import requests
2+
from docusign_esign import ApiException
3+
from flask import request, jsonify
4+
5+
from app.ds_config import DS_CONFIG
6+
from app.api import api, authenticate
7+
8+
9+
@api.route('/user/info', methods=['POST'])
10+
@authenticate
11+
def user_info():
12+
url = DS_CONFIG["authorization_server"] + "/oauth/userinfo"
13+
auth = {"Authorization": request.headers.get('Authorization')}
14+
try:
15+
response = requests.get(url, headers=auth).json()
16+
except ApiException as e:
17+
return jsonify({'error': e.body.decode('utf-8')}), 400
18+
return jsonify(response)

0 commit comments

Comments
 (0)