Skip to content

Commit 48a9782

Browse files
Merge branch 'master' into feature/webforms-heading
2 parents dbb687d + 7d1446d commit 48a9782

File tree

7 files changed

+578
-543
lines changed

7 files changed

+578
-543
lines changed

index.js

Lines changed: 391 additions & 384 deletions
Large diffs are not rendered by default.

lib/DSAuthCodeGrant.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,35 @@ DSAuthCodeGrant.prototype.login = function(req, res, next) {
6060
// Reset
6161
this.internalLogout(req, res);
6262
req.session.authMethod = 'grand-auth';
63-
passport.authenticate('docusign')(req, res, next);
63+
64+
if (req.session?.pkceFailed) {
65+
passport.authenticate('docusign')(req, res, next);
66+
} else {
67+
passport.authenticate('docusign_pkce')(req, res, next);
68+
}
6469
};
6570

6671
DSAuthCodeGrant.prototype.oauth_callback1 = (req, res, next) => {
6772
// This callback URL is used for the login flow
68-
passport.authenticate('docusign', { failureRedirect: '/ds/login' })(req, res, next);
73+
if (req.session?.pkceFailed) {
74+
passport.authenticate('docusign', { failureRedirect: '/ds/login' })(req, res, next);
75+
} else {
76+
passport.authenticate('docusign_pkce', { failureRedirect: '/ds/login' }, (err, user, _info) => {
77+
if (err || !user) { return next(); }
78+
79+
req.logIn(user, function(err) {
80+
if (err) { return next(err); }
81+
return next();
82+
});
83+
})(req, res, next);
84+
}
6985
};
7086
DSAuthCodeGrant.prototype.oauth_callback2 = function _oauth_callback2(req, res, next) {
87+
if (!req.session.pkceFailed && !req?.user?.accessToken) {
88+
req.session.pkceFailed = true;
89+
return res.redirect('/ds/login');
90+
}
91+
7192
this._accessToken = req.user.accessToken;
7293
console.log(`Received access_token: |${req.user.accessToken}|`);
7394
console.log(`Expires at ${req.user.tokenExpirationTimestamp.format('dddd, MMMM Do YYYY, h:mm:ss a')}`);

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"chai-exclude": "^2.1.1",
6565
"eslint": "^8.57.0",
6666
"eslint-config-standard": "^17.1.0",
67-
"mocha": "^10.7.0"
67+
"mocha": "^10.7.3"
6868
},
6969
"overrides": {
7070
"braces": "^3.0.3",

quick_acg/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quick_acg/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
},
4040
"devDependencies": {
4141
"chai": "^4.5.0",
42-
"mocha": "^10.7.0"
42+
"mocha": "^10.7.3"
4343
},
4444
"overrides": {
4545
"braces": "^3.0.3",

quick_acg/quickACG.js

Lines changed: 160 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,160 @@
1-
#!/usr/bin/env node
2-
3-
const express = require('express');
4-
const session = require('express-session'); // https://github.com/expressjs/session
5-
const bodyParser = require('body-parser');
6-
const cookieParser = require('cookie-parser');
7-
const MemoryStore = require('memorystore')(session); // https://github.com/roccomuso/memorystore
8-
const path = require('path');
9-
const DSAuthCodeGrant = require('../lib/DSAuthCodeGrant');
10-
const passport = require('passport');
11-
const DocusignStrategy = require('passport-docusign');
12-
const docOptions = require('../config/documentOptions.json');
13-
const docNames = require('../config/documentNames.json');
14-
const dsConfig = require('../config/index.js').config;
15-
const commonControllers = require('../lib/commonControllers');
16-
const flash = require('express-flash');
17-
const helmet = require('helmet'); // https://expressjs.com/en/advanced/best-practice-security.html
18-
const moment = require('moment');
19-
const csrf = require('csurf'); // https://www.npmjs.com/package/csurf
20-
const { getManifest } = require('../lib/manifestService');
21-
22-
const eg001 = require('../lib/eSignature/controllers/eg001EmbeddedSigning');
23-
const eg041 = require('../lib/eSignature/controllers/eg041CFREmbeddedSigning');
24-
25-
const PORT = process.env.PORT || 3000;
26-
const HOST = process.env.HOST || 'localhost';
27-
const max_session_min = 180;
28-
const csrfProtection = csrf({ cookie: true });
29-
30-
let hostUrl = 'http://' + HOST + ':' + PORT;
31-
if (dsConfig.appUrl !== '' && dsConfig.appUrl !== '{APP_URL}') { hostUrl = dsConfig.appUrl; }
32-
33-
let app = express()
34-
.use(helmet())
35-
.use(express.static(path.join(__dirname, 'public')))
36-
.use(cookieParser())
37-
.use(session({
38-
secret: dsConfig.sessionSecret,
39-
name: 'ds-launcher-session',
40-
cookie: { maxAge: max_session_min * 60000 },
41-
saveUninitialized: true,
42-
resave: true,
43-
store: new MemoryStore({
44-
checkPeriod: 86400000 // prune expired entries every 24h
45-
})
46-
}))
47-
.use(passport.initialize())
48-
.use(passport.session())
49-
.use(bodyParser.urlencoded({ extended: true }))
50-
.use((req, res, next) => {
51-
res.locals.user = req.user;
52-
res.locals.session = req.session;
53-
res.locals.dsConfig = { ...dsConfig, docOptions: docOptions, docNames: docNames};
54-
res.locals.quickACG = true;
55-
res.locals.hostUrl = hostUrl; // Used by DSAuthCodeGrant#logout
56-
next();
57-
}) // Send user info to views
58-
.use(flash())
59-
.set('views', path.join(__dirname, '../views'))
60-
.set('view engine', 'ejs')
61-
// Add an instance of DSAuthCodeGrant to req
62-
.use((req, res, next) => {
63-
req.dsAuthCodeGrant = new DSAuthCodeGrant(req);
64-
req.dsAuth = req.dsAuthCodeGrant;
65-
next();
66-
})
67-
.use(async (req, res, next) => {
68-
let manifestUrl = dsConfig.codeExamplesManifest;
69-
70-
const manifest = await getManifest(manifestUrl);
71-
res.locals.manifest = manifest;
72-
73-
next();
74-
})
75-
.use(csrfProtection) // CSRF protection for the following routes
76-
// Routes
77-
.get('/', redirectEg001)
78-
.get('/eg001', eg001.getController)
79-
.post('/eg001', eg001.createController)
80-
.get('/eg041', eg041.getController)
81-
.post('/eg041', eg041.createController)
82-
83-
.get('/ds/mustAuthenticate', redirectLogin)
84-
.get('/ds/login', commonControllers.login)
85-
.get('/ds-return', redirectReturn)
86-
.get('/ds/callback', [dsLoginCB1, dsLoginCB2]) // OAuth callbacks. See below
87-
;
88-
89-
function redirectEg001(req, res) { return res.redirect('/eg001'); }
90-
function redirectLogin(req, res) { return res.redirect('/ds/login'); }
91-
function redirectReturn(req, res) { return res.redirect('/eg001'); }
92-
function dsLoginCB1(req, res, next) { req.dsAuthCodeGrant.oauth_callback1(req, res, next); }
93-
function dsLoginCB2(req, res, next) { req.dsAuthCodeGrant.oauth_callback2(req, res, next); }
94-
95-
if (dsConfig.dsClientId && dsConfig.dsClientId !== '{CLIENT_ID}' &&
96-
dsConfig.dsClientSecret && dsConfig.dsClientSecret !== '{CLIENT_SECRET}') {
97-
app.listen(PORT);
98-
console.log(`Listening on ${PORT}`);
99-
console.log(`Ready! Open ${hostUrl}`);
100-
} else {
101-
console.log(`PROBLEM: You need to set the clientId (Integrator Key), and perhaps other settings as well.
102-
You can set them in the configuration file config/appsettings.json or set environment variables.\n`);
103-
process.exit(); // We're not using exit code of 1 to avoid extraneous npm messages.
104-
}
105-
106-
// Passport session setup.
107-
// To support persistent login sessions, Passport needs to be able to
108-
// serialize users into and deserialize users out of the session. Typically,
109-
// this will be as simple as storing the user ID when serializing, and finding
110-
// the user by ID when deserializing. However, since this example does not
111-
// have a database of user records, the complete DocuSign profile is serialized
112-
// and deserialized.
113-
passport.serializeUser(function(user, done) { done(null, user); });
114-
passport.deserializeUser(function(obj, done) { done(null, obj); });
115-
116-
let scope = ['signature'];
117-
// Configure passport for DocusignStrategy
118-
let docusignStrategy = new DocusignStrategy({
119-
production: dsConfig.production,
120-
clientID: dsConfig.dsClientId,
121-
scope: scope.join(' '),
122-
clientSecret: dsConfig.dsClientSecret,
123-
callbackURL: hostUrl + '/ds/callback',
124-
state: true // automatic CSRF protection.
125-
// See https://github.com/jaredhanson/passport-oauth2/blob/master/lib/state/session.js
126-
},
127-
function _processDsResult(accessToken, refreshToken, params, profile, done) {
128-
// The params arg will be passed additional parameters of the grant.
129-
// See https://github.com/jaredhanson/passport-oauth2/pull/84
130-
//
131-
// Here we're just assigning the tokens to the account object
132-
// We store the data in DSAuthCodeGrant.getDefaultAccountInfo
133-
let user = profile;
134-
user.accessToken = accessToken;
135-
user.refreshToken = refreshToken;
136-
user.expiresIn = params.expires_in;
137-
user.tokenExpirationTimestamp = moment().add(user.expiresIn, 's'); // The dateTime when the access token will expire
138-
return done(null, user);
139-
}
140-
);
141-
142-
/**
143-
* The DocuSign OAuth default is to allow silent authentication.
144-
* An additional OAuth query parameter is used to not allow silent authentication
145-
*/
146-
if (!dsConfig.allowSilentAuthentication) {
147-
// See https://stackoverflow.com/a/32877712/64904
148-
docusignStrategy.authorizationParams = function(options) {
149-
return { prompt: 'login' };
150-
};
151-
}
152-
153-
passport.use(docusignStrategy);
1+
#!/usr/bin/env node
2+
3+
const express = require('express');
4+
const session = require('express-session'); // https://github.com/expressjs/session
5+
const bodyParser = require('body-parser');
6+
const cookieParser = require('cookie-parser');
7+
const MemoryStore = require('memorystore')(session); // https://github.com/roccomuso/memorystore
8+
const path = require('path');
9+
const DSAuthCodeGrant = require('../lib/DSAuthCodeGrant');
10+
const passport = require('passport');
11+
const DocusignStrategy = require('passport-docusign');
12+
const docOptions = require('../config/documentOptions.json');
13+
const docNames = require('../config/documentNames.json');
14+
const dsConfig = require('../config/index.js').config;
15+
const commonControllers = require('../lib/commonControllers');
16+
const flash = require('express-flash');
17+
const helmet = require('helmet'); // https://expressjs.com/en/advanced/best-practice-security.html
18+
const moment = require('moment');
19+
const csrf = require('csurf'); // https://www.npmjs.com/package/csurf
20+
const { getManifest } = require('../lib/manifestService');
21+
22+
const eg001 = require('../lib/eSignature/controllers/eg001EmbeddedSigning');
23+
const eg041 = require('../lib/eSignature/controllers/eg041CFREmbeddedSigning');
24+
25+
const PORT = process.env.PORT || 3000;
26+
const HOST = process.env.HOST || 'localhost';
27+
const max_session_min = 180;
28+
const csrfProtection = csrf({ cookie: true });
29+
30+
let hostUrl = 'http://' + HOST + ':' + PORT;
31+
if (dsConfig.appUrl !== '' && dsConfig.appUrl !== '{APP_URL}') { hostUrl = dsConfig.appUrl; }
32+
33+
let app = express()
34+
.use(helmet())
35+
.use(express.static(path.join(__dirname, 'public')))
36+
.use(cookieParser())
37+
.use(session({
38+
secret: dsConfig.sessionSecret,
39+
name: 'ds-launcher-session',
40+
cookie: { maxAge: max_session_min * 60000 },
41+
saveUninitialized: true,
42+
resave: true,
43+
store: new MemoryStore({
44+
checkPeriod: 86400000 // prune expired entries every 24h
45+
})
46+
}))
47+
.use(passport.initialize())
48+
.use(passport.session())
49+
.use(bodyParser.urlencoded({ extended: true }))
50+
.use((req, res, next) => {
51+
res.locals.user = req.user;
52+
res.locals.session = req.session;
53+
res.locals.dsConfig = { ...dsConfig, docOptions: docOptions, docNames: docNames};
54+
res.locals.quickACG = true;
55+
res.locals.hostUrl = hostUrl; // Used by DSAuthCodeGrant#logout
56+
next();
57+
}) // Send user info to views
58+
.use(flash())
59+
.set('views', path.join(__dirname, '../views'))
60+
.set('view engine', 'ejs')
61+
// Add an instance of DSAuthCodeGrant to req
62+
.use((req, res, next) => {
63+
req.dsAuthCodeGrant = new DSAuthCodeGrant(req);
64+
req.dsAuth = req.dsAuthCodeGrant;
65+
next();
66+
})
67+
.use(async (req, res, next) => {
68+
let manifestUrl = dsConfig.codeExamplesManifest;
69+
70+
const manifest = await getManifest(manifestUrl);
71+
res.locals.manifest = manifest;
72+
73+
next();
74+
})
75+
.use(csrfProtection) // CSRF protection for the following routes
76+
// Routes
77+
.get('/', redirectEg001)
78+
.get('/eg001', eg001.getController)
79+
.post('/eg001', eg001.createController)
80+
.get('/eg041', eg041.getController)
81+
.post('/eg041', eg041.createController)
82+
83+
.get('/ds/mustAuthenticate', redirectLogin)
84+
.get('/ds/login', commonControllers.login)
85+
.get('/ds-return', redirectReturn)
86+
.get('/ds/callback', [dsLoginCB1, dsLoginCB2]) // OAuth callbacks. See below
87+
;
88+
89+
function redirectEg001(req, res) { return res.redirect('/eg001'); }
90+
function redirectLogin(req, res) { return res.redirect('/ds/login'); }
91+
function redirectReturn(req, res) { return res.redirect('/eg001'); }
92+
function dsLoginCB1(req, res, next) { req.dsAuthCodeGrant.oauth_callback1(req, res, next); }
93+
function dsLoginCB2(req, res, next) { req.dsAuthCodeGrant.oauth_callback2(req, res, next); }
94+
95+
if (dsConfig.dsClientId && dsConfig.dsClientId !== '{CLIENT_ID}' &&
96+
dsConfig.dsClientSecret && dsConfig.dsClientSecret !== '{CLIENT_SECRET}') {
97+
app.listen(PORT);
98+
console.log(`Listening on ${PORT}`);
99+
console.log(`Ready! Open ${hostUrl}`);
100+
} else {
101+
console.log(`PROBLEM: You need to set the clientId (Integrator Key), and perhaps other settings as well.
102+
You can set them in the configuration file config/appsettings.json or set environment variables.\n`);
103+
process.exit(); // We're not using exit code of 1 to avoid extraneous npm messages.
104+
}
105+
106+
// Passport session setup.
107+
// To support persistent login sessions, Passport needs to be able to
108+
// serialize users into and deserialize users out of the session. Typically,
109+
// this will be as simple as storing the user ID when serializing, and finding
110+
// the user by ID when deserializing. However, since this example does not
111+
// have a database of user records, the complete DocuSign profile is serialized
112+
// and deserialized.
113+
passport.serializeUser(function(user, done) { done(null, user); });
114+
passport.deserializeUser(function(obj, done) { done(null, obj); });
115+
116+
let scope = ['signature'];
117+
// Configure passport for DocusignStrategy
118+
const docusignStrategyOptions = {
119+
production: dsConfig.production,
120+
clientID: dsConfig.dsClientId,
121+
scope: scope.join(' '),
122+
clientSecret: dsConfig.dsClientSecret,
123+
callbackURL: hostUrl + '/ds/callback',
124+
state: true // automatic CSRF protection.
125+
// See https://github.com/jaredhanson/passport-oauth2/blob/master/lib/state/session.js
126+
};
127+
function processDsResult(accessToken, refreshToken, params, profile, done) {
128+
// The params arg will be passed additional parameters of the grant.
129+
// See https://github.com/jaredhanson/passport-oauth2/pull/84
130+
//
131+
// Here we're just assigning the tokens to the account object
132+
// We store the data in DSAuthCodeGrant.getDefaultAccountInfo
133+
let user = profile;
134+
user.accessToken = accessToken;
135+
user.refreshToken = refreshToken;
136+
user.expiresIn = params.expires_in;
137+
user.tokenExpirationTimestamp = moment().add(user.expiresIn, 's'); // The dateTime when the access token will expire
138+
return done(null, user);
139+
}
140+
const docusignStrategy = new DocusignStrategy(docusignStrategyOptions, processDsResult);
141+
const docusignStrategyPKCE = new DocusignStrategy({
142+
...docusignStrategyOptions,
143+
pkce: true
144+
},
145+
processDsResult
146+
);
147+
148+
/**
149+
* The DocuSign OAuth default is to allow silent authentication.
150+
* An additional OAuth query parameter is used to not allow silent authentication
151+
*/
152+
if (!dsConfig.allowSilentAuthentication) {
153+
// See https://stackoverflow.com/a/32877712/64904
154+
docusignStrategy.authorizationParams = function(options) {
155+
return { prompt: 'login' };
156+
};
157+
}
158+
159+
passport.use('docusign', docusignStrategy);
160+
passport.use('docusign_pkce', docusignStrategyPKCE);

0 commit comments

Comments
 (0)