Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a11e137

Browse files
yihong0618jdneo
authored andcommitted
solve GitHub login two-factor auth problem (leetcode-tools#35)
1 parent dae0c8e commit a11e137

File tree

4 files changed

+109
-64
lines changed

4 files changed

+109
-64
lines changed

‎lib/config.js

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,36 @@ const DEFAULT_CONFIG = {
3131
'swift'
3232
],
3333
urls: {
34-
base: 'https://leetcode.com',
35-
graphql: 'https://leetcode.com/graphql',
36-
login: 'https://leetcode.com/accounts/login/',
37-
// third part login base urls. TODO facebook google
38-
github_login: 'https://leetcode.com/accounts/github/login/?next=%2F',
39-
facebook_login: 'https://leetcode.com/accounts/facebook/login/?next=%2F',
40-
linkedin_login: 'https://leetcode.com/accounts/linkedin_oauth2/login/?next=%2F',
41-
problems: 'https://leetcode.com/api/problems/$category/',
42-
problem: 'https://leetcode.com/problems/$slug/description/',
43-
test: 'https://leetcode.com/problems/$slug/interpret_solution/',
44-
session: 'https://leetcode.com/session/',
45-
submit: 'https://leetcode.com/problems/$slug/submit/',
46-
submissions: 'https://leetcode.com/api/submissions/$slug',
47-
submission: 'https://leetcode.com/submissions/detail/$id/',
48-
verify: 'https://leetcode.com/submissions/detail/$id/check/',
49-
favorites: 'https://leetcode.com/list/api/questions',
50-
favorite_delete: 'https://leetcode.com/list/api/questions/$hash/$id',
51-
plugin: 'https://raw.githubusercontent.com/leetcode-tools/leetcode-cli-plugins/master/plugins/$name.js'
52-
}
34+
// base urls
35+
base: 'https://leetcode.com',
36+
graphql: 'https://leetcode.com/graphql',
37+
login: 'https://leetcode.com/accounts/login/',
38+
// third part login base urls. TODO facebook google
39+
github_login: 'https://leetcode.com/accounts/github/login/?next=%2F',
40+
facebook_login: 'https://leetcode.com/accounts/facebook/login/?next=%2F',
41+
linkedin_login: 'https://leetcode.com/accounts/linkedin_oauth2/login/?next=%2F',
42+
// redirect urls
43+
leetcode_redirect: 'https://leetcode.com/',
44+
github_tf_redirect: 'https://github.com/sessions/two-factor',
45+
// simulate login urls
46+
github_login_request: 'https://github.com/login',
47+
github_session_request: 'https://github.com/session',
48+
github_tf_session_request: 'https://github.com/sessions/two-factor',
49+
linkedin_login_request: 'https://www.linkedin.com',
50+
linkedin_session_request: 'https://www.linkedin.com/uas/login-submit',
51+
// questions urls
52+
problems: 'https://leetcode.com/api/problems/$category/',
53+
problem: 'https://leetcode.com/problems/$slug/description/',
54+
test: 'https://leetcode.com/problems/$slug/interpret_solution/',
55+
session: 'https://leetcode.com/session/',
56+
submit: 'https://leetcode.com/problems/$slug/submit/',
57+
submissions: 'https://leetcode.com/api/submissions/$slug',
58+
submission: 'https://leetcode.com/submissions/detail/$id/',
59+
verify: 'https://leetcode.com/submissions/detail/$id/check/',
60+
favorites: 'https://leetcode.com/list/api/questions',
61+
favorite_delete: 'https://leetcode.com/list/api/questions/$hash/$id',
62+
plugin: 'https://raw.githubusercontent.com/leetcode-tools/leetcode-cli-plugins/master/plugins/$name.js'
63+
},
5364
},
5465

5566
// but you will want change these

‎lib/plugins/leetcode.cn.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,24 @@ var plugin = new Plugin(15, 'leetcode.cn', '2018年11月25日',
1717

1818
plugin.init = function() {
1919
config.app = 'leetcode.cn';
20-
config.sys.urls.base = 'https://leetcode-cn.com';
21-
config.sys.urls.login = 'https://leetcode-cn.com/accounts/login/';
22-
config.sys.urls.problems = 'https://leetcode-cn.com/api/problems/$category/';
23-
config.sys.urls.problem = 'https://leetcode-cn.com/problems/$slug/description/';
24-
config.sys.urls.graphql = 'https://leetcode-cn.com/graphql';
25-
config.sys.urls.problem_detail = 'https://leetcode-cn.com/graphql';
26-
config.sys.urls.test = 'https://leetcode-cn.com/problems/$slug/interpret_solution/';
27-
config.sys.urls.session = 'https://leetcode-cn.com/session/';
28-
config.sys.urls.submit = 'https://leetcode-cn.com/problems/$slug/submit/';
29-
config.sys.urls.submissions = 'https://leetcode-cn.com/api/submissions/$slug';
30-
config.sys.urls.submission = 'https://leetcode-cn.com/submissions/detail/$id/';
31-
config.sys.urls.verify = 'https://leetcode-cn.com/submissions/detail/$id/check/';
32-
config.sys.urls.favorites = 'https://leetcode-cn.com/list/api/questions';
33-
config.sys.urls.favorite_delete = 'https://leetcode-cn.com/list/api/questions/$hash/$id';
20+
config.sys.urls.base = 'https://leetcode-cn.com';
21+
config.sys.urls.login = 'https://leetcode-cn.com/accounts/login/';
22+
config.sys.urls.problems = 'https://leetcode-cn.com/api/problems/$category/';
23+
config.sys.urls.problem = 'https://leetcode-cn.com/problems/$slug/description/';
24+
config.sys.urls.graphql = 'https://leetcode-cn.com/graphql';
25+
config.sys.urls.problem_detail = 'https://leetcode-cn.com/graphql';
26+
config.sys.urls.test = 'https://leetcode-cn.com/problems/$slug/interpret_solution/';
27+
config.sys.urls.session = 'https://leetcode-cn.com/session/';
28+
config.sys.urls.submit = 'https://leetcode-cn.com/problems/$slug/submit/';
29+
config.sys.urls.submissions = 'https://leetcode-cn.com/api/submissions/$slug';
30+
config.sys.urls.submission = 'https://leetcode-cn.com/submissions/detail/$id/';
31+
config.sys.urls.verify = 'https://leetcode-cn.com/submissions/detail/$id/check/';
32+
config.sys.urls.favorites = 'https://leetcode-cn.com/list/api/questions';
33+
config.sys.urls.favorite_delete = 'https://leetcode-cn.com/list/api/questions/$hash/$id';
34+
// third parties
35+
config.sys.urls.github_login = 'https://leetcode-cn.com/accounts/github/login/?next=%2F';
36+
config.sys.urls.linkedin_login = 'https://leetcode-cn.com/accounts/linkedin_oauth2/login/?next=%2F';
37+
config.sys.urls.leetcode_redirect = 'https://leetcode-cn.com/';
3438
};
3539

3640
// FIXME: refactor those

‎lib/plugins/leetcode.js

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ plugin.getSubmissions = function(problem, cb) {
333333

334334
// FIXME: this only return the 1st 20 submissions, we should get next if necessary.
335335
const submissions = JSON.parse(body).submissions_dump;
336-
for (let submission of submissions)
336+
for (const submission of submissions)
337337
submission.id = _.last(_.compact(submission.url.split('/')));
338338

339339
return cb(null, submissions);
@@ -471,8 +471,8 @@ plugin.deleteSession = function(session, cb) {
471471
};
472472

473473
plugin.signin = function(user, cb) {
474-
log.debug('running leetcode.signin');
475-
const spin = h.spin('Signing in leetcode.com');
474+
constisCN=config.app==='leetcode.cn';
475+
const spin = isCN ? h.spin('Signing in leetcode-cn.com') : h.spin('Signing in leetcode.com');
476476
request(config.sys.urls.login, function(e, resp, body) {
477477
spin.stop();
478478
e = plugin.checkError(e, resp, 200);
@@ -538,11 +538,18 @@ plugin.login = function(user, cb) {
538538
});
539539
};
540540

541-
function parseCookie(cookie, cb) {
541+
function parseCookie(cookie, body, cb) {
542+
const isCN = config.app === 'leetcode.cn';
542543
const SessionPattern = /LEETCODE_SESSION=(.+?)(;|$)/;
543-
const csrfPattern = /csrftoken=(.+?)(;|$)/;
544+
let csrfPattern;
545+
// leetcode-cn.com Cookie is not the same as leetcode.com in third parties
546+
if (isCN) {
547+
csrfPattern = /name="csrfmiddlewaretoken"value="(.*?)"/;
548+
} else {
549+
csrfPattern = /csrftoken=(.+?)(;|$)/;
550+
}
544551
const reSessionResult = SessionPattern.exec(cookie);
545-
const reCsrfResult = csrfPattern.exec(cookie);
552+
const reCsrfResult = csrfPattern.exec(isCN? body: cookie);
546553
if (reSessionResult === null || reCsrfResult === null) {
547554
return cb('invalid cookie?');
548555
}
@@ -552,11 +559,18 @@ function parseCookie(cookie, cb) {
552559
};
553560
}
554561

555-
function saveAndGetUser(user, cb, cookieData) {
556-
user.sessionId = cookieData.sessionId;
557-
user.sessionCSRF = cookieData.sessionCSRF;
558-
session.saveUser(user);
559-
plugin.getUser(user, cb);
562+
function requestLeetcodeAndSave(request, leetcodeUrl, user, cb) {
563+
request.get({url: leetcodeUrl}, function(e, resp, body) {
564+
const redirectUri = resp.request.uri.href;
565+
if (redirectUri !== config.sys.urls.leetcode_redirect) {
566+
return cb('Login failed. Please make sure the credential is correct.');
567+
}
568+
const cookieData = parseCookie(resp.request.headers.cookie, body, cb);
569+
user.sessionId = cookieData.sessionId;
570+
user.sessionCSRF = cookieData.sessionCSRF;
571+
session.saveUser(user);
572+
plugin.getUser(user, cb);
573+
});
560574
}
561575

562576
plugin.cookieLogin = function(user, cb) {
@@ -568,15 +582,16 @@ plugin.cookieLogin = function(user, cb) {
568582
};
569583

570584
plugin.githubLogin = function(user, cb) {
571-
const leetcodeUrl = config.sys.urls.github_login;
585+
const urls = config.sys.urls;
586+
const leetcodeUrl = urls.github_login;
572587
const _request = request.defaults({jar: true});
573-
_request('https://github.com/login', function(e, resp, body) {
588+
_request(urls.github_login_request, function(e, resp, body) {
574589
const authenticityToken = body.match(/name="authenticity_token"value="(.*?)"/);
575590
if (authenticityToken === null) {
576591
return cb('Get GitHub token failed');
577592
}
578593
const options = {
579-
url: 'https://github.com/session',
594+
url: urls.github_session_request,
580595
method: 'POST',
581596
headers: {
582597
'Content-Type': 'application/x-www-form-urlencoded',
@@ -594,27 +609,48 @@ plugin.githubLogin = function(user, cb) {
594609
if (resp.statusCode !== 200) {
595610
return cb('GitHub login failed');
596611
}
597-
_request.get({url: leetcodeUrl}, function(e, resp, body) {
598-
const redirectUri = resp.request.uri.href;
599-
if (redirectUri !== 'https://leetcode.com/') {
600-
return cb('GitHub login failed or GitHub did not link to LeetCode');
612+
if (resp.request.uri.href !== urls.github_tf_redirect) {
613+
return requestLeetcodeAndSave(_request, leetcodeUrl, user, cb);
614+
}
615+
// read two-factor code must be sync.
616+
const twoFactorcode = require('prompt-sync')()('Please enter your two-factor code: ');
617+
const authenticityTokenTwoFactor = body.match(/name="authenticity_token"value="(.*?)"/);
618+
if (authenticityTokenTwoFactor === null) {
619+
return cb('Get GitHub two-factor token failed');
620+
}
621+
const optionsTwoFactor = {
622+
url: urls.github_tf_session_request,
623+
method: 'POST',
624+
headers: {
625+
'Content-Type': 'application/x-www-form-urlencoded',
626+
},
627+
followAllRedirects: true,
628+
form: {
629+
'otp': twoFactorcode,
630+
'authenticity_token': authenticityTokenTwoFactor[1],
631+
'utf8': encodeURIComponent('✓'),
632+
},
633+
};
634+
_request(optionsTwoFactor, function(e, resp, body) {
635+
if (resp.request.uri.href === urls.github_tf_session_request) {
636+
return cb('Invalid two-factor code please check');
601637
}
602-
const cookieData = parseCookie(resp.request.headers.cookie, cb);
603-
saveAndGetUser(user, cb, cookieData);
638+
requestLeetcodeAndSave(_request, leetcodeUrl, user, cb);
604639
});
605640
});
606641
});
607642
};
608643

609644
plugin.linkedinLogin = function(user, cb) {
610-
const leetcodeUrl = config.sys.urls.linkedin_login;
645+
const urls = config.sys.urls;
646+
const leetcodeUrl = urls.linkedin_login;
611647
const _request = request.defaults({
612648
jar: true,
613649
headers: {
614650
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
615651
}
616652
});
617-
_request('https://www.linkedin.com', function(e, resp, body) {
653+
_request(urls.linkedin_login_request, function(e, resp, body) {
618654
if ( resp.statusCode !== 200) {
619655
return cb('Get LinkedIn session failed');
620656
}
@@ -623,7 +659,7 @@ plugin.linkedinLogin = function(user, cb) {
623659
return cb('Get LinkedIn token failed');
624660
}
625661
const options = {
626-
url: 'https://www.linkedin.com/uas/login-submit',
662+
url: urls.linkedin_session_request,
627663
method: 'POST',
628664
headers: {
629665
'Content-Type': 'application/x-www-form-urlencoded',
@@ -640,14 +676,7 @@ plugin.linkedinLogin = function(user, cb) {
640676
if (resp.statusCode !== 200) {
641677
return cb('LinkedIn login failed');
642678
}
643-
_request.get({url: leetcodeUrl}, function(e, resp, body) {
644-
const redirectUri = resp.request.uri.href;
645-
if (redirectUri !== 'https://leetcode.com/') {
646-
return cb('LinkedIn login failed or LinkedIn did not link to LeetCode');
647-
}
648-
const cookieData = parseCookie(resp.request.headers.cookie, cb);
649-
saveAndGetUser(user, cb, cookieData);
650-
});
679+
requestLeetcodeAndSave(_request, leetcodeUrl, user, cb);
651680
});
652681
});
653682
};

‎package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"nock": "10.0.2",
7272
"nyc": "^13.3.0",
7373
"pkg": "^4.3.4",
74+
"prompt-sync": "^4.2.0",
7475
"rewire": "4.0.1"
7576
}
7677
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /