Skip to content

Commit

Permalink
solve GitHub login two-factor auth problem (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
yihong0618 authored and jdneo committed Dec 30, 2019
1 parent dae0c8e commit a11e137
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 64 deletions.
49 changes: 30 additions & 19 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,36 @@ const DEFAULT_CONFIG = {
'swift'
],
urls: {
base: 'https://leetcode.com',
graphql: 'https://leetcode.com/graphql',
login: 'https://leetcode.com/accounts/login/',
// third part login base urls. TODO facebook google
github_login: 'https://leetcode.com/accounts/github/login/?next=%2F',
facebook_login: 'https://leetcode.com/accounts/facebook/login/?next=%2F',
linkedin_login: 'https://leetcode.com/accounts/linkedin_oauth2/login/?next=%2F',
problems: 'https://leetcode.com/api/problems/$category/',
problem: 'https://leetcode.com/problems/$slug/description/',
test: 'https://leetcode.com/problems/$slug/interpret_solution/',
session: 'https://leetcode.com/session/',
submit: 'https://leetcode.com/problems/$slug/submit/',
submissions: 'https://leetcode.com/api/submissions/$slug',
submission: 'https://leetcode.com/submissions/detail/$id/',
verify: 'https://leetcode.com/submissions/detail/$id/check/',
favorites: 'https://leetcode.com/list/api/questions',
favorite_delete: 'https://leetcode.com/list/api/questions/$hash/$id',
plugin: 'https://raw.githubusercontent.com/leetcode-tools/leetcode-cli-plugins/master/plugins/$name.js'
}
// base urls
base: 'https://leetcode.com',
graphql: 'https://leetcode.com/graphql',
login: 'https://leetcode.com/accounts/login/',
// third part login base urls. TODO facebook google
github_login: 'https://leetcode.com/accounts/github/login/?next=%2F',
facebook_login: 'https://leetcode.com/accounts/facebook/login/?next=%2F',
linkedin_login: 'https://leetcode.com/accounts/linkedin_oauth2/login/?next=%2F',
// redirect urls
leetcode_redirect: 'https://leetcode.com/',
github_tf_redirect: 'https://github.com/sessions/two-factor',
// simulate login urls
github_login_request: 'https://github.com/login',
github_session_request: 'https://github.com/session',
github_tf_session_request: 'https://github.com/sessions/two-factor',
linkedin_login_request: 'https://www.linkedin.com',
linkedin_session_request: 'https://www.linkedin.com/uas/login-submit',
// questions urls
problems: 'https://leetcode.com/api/problems/$category/',
problem: 'https://leetcode.com/problems/$slug/description/',
test: 'https://leetcode.com/problems/$slug/interpret_solution/',
session: 'https://leetcode.com/session/',
submit: 'https://leetcode.com/problems/$slug/submit/',
submissions: 'https://leetcode.com/api/submissions/$slug',
submission: 'https://leetcode.com/submissions/detail/$id/',
verify: 'https://leetcode.com/submissions/detail/$id/check/',
favorites: 'https://leetcode.com/list/api/questions',
favorite_delete: 'https://leetcode.com/list/api/questions/$hash/$id',
plugin: 'https://raw.githubusercontent.com/leetcode-tools/leetcode-cli-plugins/master/plugins/$name.js'
},
},

// but you will want change these
Expand Down
32 changes: 18 additions & 14 deletions lib/plugins/leetcode.cn.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,24 @@ var plugin = new Plugin(15, 'leetcode.cn', '2018.11.25',

plugin.init = function() {
config.app = 'leetcode.cn';
config.sys.urls.base = 'https://leetcode-cn.com';
config.sys.urls.login = 'https://leetcode-cn.com/accounts/login/';
config.sys.urls.problems = 'https://leetcode-cn.com/api/problems/$category/';
config.sys.urls.problem = 'https://leetcode-cn.com/problems/$slug/description/';
config.sys.urls.graphql = 'https://leetcode-cn.com/graphql';
config.sys.urls.problem_detail = 'https://leetcode-cn.com/graphql';
config.sys.urls.test = 'https://leetcode-cn.com/problems/$slug/interpret_solution/';
config.sys.urls.session = 'https://leetcode-cn.com/session/';
config.sys.urls.submit = 'https://leetcode-cn.com/problems/$slug/submit/';
config.sys.urls.submissions = 'https://leetcode-cn.com/api/submissions/$slug';
config.sys.urls.submission = 'https://leetcode-cn.com/submissions/detail/$id/';
config.sys.urls.verify = 'https://leetcode-cn.com/submissions/detail/$id/check/';
config.sys.urls.favorites = 'https://leetcode-cn.com/list/api/questions';
config.sys.urls.favorite_delete = 'https://leetcode-cn.com/list/api/questions/$hash/$id';
config.sys.urls.base = 'https://leetcode-cn.com';
config.sys.urls.login = 'https://leetcode-cn.com/accounts/login/';
config.sys.urls.problems = 'https://leetcode-cn.com/api/problems/$category/';
config.sys.urls.problem = 'https://leetcode-cn.com/problems/$slug/description/';
config.sys.urls.graphql = 'https://leetcode-cn.com/graphql';
config.sys.urls.problem_detail = 'https://leetcode-cn.com/graphql';
config.sys.urls.test = 'https://leetcode-cn.com/problems/$slug/interpret_solution/';
config.sys.urls.session = 'https://leetcode-cn.com/session/';
config.sys.urls.submit = 'https://leetcode-cn.com/problems/$slug/submit/';
config.sys.urls.submissions = 'https://leetcode-cn.com/api/submissions/$slug';
config.sys.urls.submission = 'https://leetcode-cn.com/submissions/detail/$id/';
config.sys.urls.verify = 'https://leetcode-cn.com/submissions/detail/$id/check/';
config.sys.urls.favorites = 'https://leetcode-cn.com/list/api/questions';
config.sys.urls.favorite_delete = 'https://leetcode-cn.com/list/api/questions/$hash/$id';
// third parties
config.sys.urls.github_login = 'https://leetcode-cn.com/accounts/github/login/?next=%2F';
config.sys.urls.linkedin_login = 'https://leetcode-cn.com/accounts/linkedin_oauth2/login/?next=%2F';
config.sys.urls.leetcode_redirect = 'https://leetcode-cn.com/';
};

// FIXME: refactor those
Expand Down
91 changes: 60 additions & 31 deletions lib/plugins/leetcode.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ plugin.getSubmissions = function(problem, cb) {

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

return cb(null, submissions);
Expand Down Expand Up @@ -471,8 +471,8 @@ plugin.deleteSession = function(session, cb) {
};

plugin.signin = function(user, cb) {
log.debug('running leetcode.signin');
const spin = h.spin('Signing in leetcode.com');
const isCN = config.app === 'leetcode.cn';
const spin = isCN ? h.spin('Signing in leetcode-cn.com') : h.spin('Signing in leetcode.com');
request(config.sys.urls.login, function(e, resp, body) {
spin.stop();
e = plugin.checkError(e, resp, 200);
Expand Down Expand Up @@ -538,11 +538,18 @@ plugin.login = function(user, cb) {
});
};

function parseCookie(cookie, cb) {
function parseCookie(cookie, body, cb) {
const isCN = config.app === 'leetcode.cn';
const SessionPattern = /LEETCODE_SESSION=(.+?)(;|$)/;
const csrfPattern = /csrftoken=(.+?)(;|$)/;
let csrfPattern;
// leetcode-cn.com Cookie is not the same as leetcode.com in third parties
if (isCN) {
csrfPattern = /name="csrfmiddlewaretoken" value="(.*?)"/;
} else {
csrfPattern = /csrftoken=(.+?)(;|$)/;
}
const reSessionResult = SessionPattern.exec(cookie);
const reCsrfResult = csrfPattern.exec(cookie);
const reCsrfResult = csrfPattern.exec(isCN? body: cookie);
if (reSessionResult === null || reCsrfResult === null) {
return cb('invalid cookie?');
}
Expand All @@ -552,11 +559,18 @@ function parseCookie(cookie, cb) {
};
}

function saveAndGetUser(user, cb, cookieData) {
user.sessionId = cookieData.sessionId;
user.sessionCSRF = cookieData.sessionCSRF;
session.saveUser(user);
plugin.getUser(user, cb);
function requestLeetcodeAndSave(request, leetcodeUrl, user, cb) {
request.get({url: leetcodeUrl}, function(e, resp, body) {
const redirectUri = resp.request.uri.href;
if (redirectUri !== config.sys.urls.leetcode_redirect) {
return cb('Login failed. Please make sure the credential is correct.');
}
const cookieData = parseCookie(resp.request.headers.cookie, body, cb);
user.sessionId = cookieData.sessionId;
user.sessionCSRF = cookieData.sessionCSRF;
session.saveUser(user);
plugin.getUser(user, cb);
});
}

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

plugin.githubLogin = function(user, cb) {
const leetcodeUrl = config.sys.urls.github_login;
const urls = config.sys.urls;
const leetcodeUrl = urls.github_login;
const _request = request.defaults({jar: true});
_request('https://github.com/login', function(e, resp, body) {
_request(urls.github_login_request, function(e, resp, body) {
const authenticityToken = body.match(/name="authenticity_token" value="(.*?)"/);
if (authenticityToken === null) {
return cb('Get GitHub token failed');
}
const options = {
url: 'https://github.com/session',
url: urls.github_session_request,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Expand All @@ -594,27 +609,48 @@ plugin.githubLogin = function(user, cb) {
if (resp.statusCode !== 200) {
return cb('GitHub login failed');
}
_request.get({url: leetcodeUrl}, function(e, resp, body) {
const redirectUri = resp.request.uri.href;
if (redirectUri !== 'https://leetcode.com/') {
return cb('GitHub login failed or GitHub did not link to LeetCode');
if (resp.request.uri.href !== urls.github_tf_redirect) {
return requestLeetcodeAndSave(_request, leetcodeUrl, user, cb);
}
// read two-factor code must be sync.
const twoFactorcode = require('prompt-sync')()('Please enter your two-factor code: ');
const authenticityTokenTwoFactor = body.match(/name="authenticity_token" value="(.*?)"/);
if (authenticityTokenTwoFactor === null) {
return cb('Get GitHub two-factor token failed');
}
const optionsTwoFactor = {
url: urls.github_tf_session_request,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
followAllRedirects: true,
form: {
'otp': twoFactorcode,
'authenticity_token': authenticityTokenTwoFactor[1],
'utf8': encodeURIComponent('✓'),
},
};
_request(optionsTwoFactor, function(e, resp, body) {
if (resp.request.uri.href === urls.github_tf_session_request) {
return cb('Invalid two-factor code please check');
}
const cookieData = parseCookie(resp.request.headers.cookie, cb);
saveAndGetUser(user, cb, cookieData);
requestLeetcodeAndSave(_request, leetcodeUrl, user, cb);
});
});
});
};

plugin.linkedinLogin = function(user, cb) {
const leetcodeUrl = config.sys.urls.linkedin_login;
const urls = config.sys.urls;
const leetcodeUrl = urls.linkedin_login;
const _request = request.defaults({
jar: true,
headers: {
'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'
}
});
_request('https://www.linkedin.com', function(e, resp, body) {
_request(urls.linkedin_login_request, function(e, resp, body) {
if ( resp.statusCode !== 200) {
return cb('Get LinkedIn session failed');
}
Expand All @@ -623,7 +659,7 @@ plugin.linkedinLogin = function(user, cb) {
return cb('Get LinkedIn token failed');
}
const options = {
url: 'https://www.linkedin.com/uas/login-submit',
url: urls.linkedin_session_request,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Expand All @@ -640,14 +676,7 @@ plugin.linkedinLogin = function(user, cb) {
if (resp.statusCode !== 200) {
return cb('LinkedIn login failed');
}
_request.get({url: leetcodeUrl}, function(e, resp, body) {
const redirectUri = resp.request.uri.href;
if (redirectUri !== 'https://leetcode.com/') {
return cb('LinkedIn login failed or LinkedIn did not link to LeetCode');
}
const cookieData = parseCookie(resp.request.headers.cookie, cb);
saveAndGetUser(user, cb, cookieData);
});
requestLeetcodeAndSave(_request, leetcodeUrl, user, cb);
});
});
};
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"nock": "10.0.2",
"nyc": "^13.3.0",
"pkg": "^4.3.4",
"prompt-sync": "^4.2.0",
"rewire": "4.0.1"
}
}

0 comments on commit a11e137

Please sign in to comment.