From ecb07e7b020c60dfc7ea7b94f0f85657f60836dc Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sat, 16 Jul 2022 08:02:04 -0400 Subject: [PATCH] Moved API requests out of route handlers --- src/middleware/pnid.js | 47 ++----------- src/routes/account.js | 147 ++++++++++++++--------------------------- src/util.js | 85 ++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 136 deletions(-) diff --git a/src/middleware/pnid.js b/src/middleware/pnid.js index b91482c..39a7382 100644 --- a/src/middleware/pnid.js +++ b/src/middleware/pnid.js @@ -7,48 +7,15 @@ async function pnidMiddleware(request, response, next) { return response.redirect(`/account/login?redirect=${request.originalUrl}`); } - // Attempt to get user data - let apiResponse = await util.apiGetRequest('/v1/user', { - 'Authorization': `${request.cookies.token_type} ${request.cookies.access_token}` - }); + try { + request.account = await util.getUserAccountData(request, response); + request.pnid = await database.PNID.findOne({ pid: request.account.pid }); - if (apiResponse.statusCode !== 200) { - // Assume expired, refresh and retry request - apiResponse = await util.apiPostGetRequest('/v1/login', {}, { - refresh_token: request.cookies.refresh_token, - grant_type: 'refresh_token' - }); - - if (apiResponse.statusCode !== 200) { - // TODO: Error message - return response.status(apiResponse.statusCode).json({ - error: 'Bad' - }); - } - - const tokens = apiResponse.body; - - response.cookie('refresh_token', tokens.refresh_token, { domain: '.pretendo.network' }); - response.cookie('access_token', tokens.access_token, { domain: '.pretendo.network' }); - response.cookie('token_type', tokens.token_type, { domain: '.pretendo.network' }); - - apiResponse = await util.apiGetRequest('/v1/user', { - 'Authorization': `${tokens.token_type} ${tokens.access_token}` - }); + return next(); + } catch (error) { + response.cookie('error_message', error.message, { domain: '.pretendo.network' }); + return response.redirect('/account/login'); } - - // If still failed, something went horribly wrong - if (apiResponse.statusCode !== 200) { - // TODO: Error message - return response.status(apiResponse.statusCode).json({ - error: 'Bad' - }); - } - - request.account = apiResponse.body; - request.pnid = await database.PNID.findOne({ pid: request.account.pid }); - - return next(); } module.exports = pnidMiddleware; \ No newline at end of file diff --git a/src/routes/account.js b/src/routes/account.js index abe167f..534e55c 100644 --- a/src/routes/account.js +++ b/src/routes/account.js @@ -79,40 +79,32 @@ router.get('/login', async (request, response) => { router.post('/login', async (request, response) => { const { username, password } = request.body; - let apiResponse = await util.apiPostGetRequest('/v1/login', {}, { - username, - password, - grant_type: 'password' - }); + try { + const tokens = await util.login(username, password); - if (apiResponse.statusCode !== 200) { - response.cookie('error_message', apiResponse.body.error, { domain: '.pretendo.network' }); + response.cookie('refresh_token', tokens.refresh_token, { domain: '.pretendo.network' }); + response.cookie('access_token', tokens.access_token, { domain: '.pretendo.network' }); + response.cookie('token_type', tokens.token_type, { domain: '.pretendo.network' }); + + const account = await util.getUserAccountData(request, response); + + const hashedPassword = util.nintendoPasswordHash(password, account.pid); + const hashedPasswordBuffer = Buffer.from(hashedPassword, 'hex'); + + const cipher = crypto.createCipheriv('aes-256-cbc', aesKey, Buffer.alloc(16)); + + let encryptedBody = cipher.update(hashedPasswordBuffer); + encryptedBody = Buffer.concat([encryptedBody, cipher.final()]); + + response.cookie('ph', encryptedBody.toString('hex'), { domain: '.pretendo.network' }); + + response.redirect(request.redirect || '/account'); + + } catch (error) { + console.log(error); + response.cookie('error_message', error.message, { domain: '.pretendo.network' }); return response.redirect('/account/login'); } - - const tokens = apiResponse.body; - - response.cookie('refresh_token', tokens.refresh_token, { domain : '.pretendo.network' }); - response.cookie('access_token', tokens.access_token, { domain : '.pretendo.network' }); - response.cookie('token_type', tokens.token_type, { domain : '.pretendo.network' }); - - apiResponse = await util.apiGetRequest('/v1/user', { - 'Authorization': `${tokens.token_type} ${tokens.access_token}` - }); - - const account = apiResponse.body; - - const hashedPassword = util.nintendoPasswordHash(password, account.pid); - const hashedPasswordBuffer = Buffer.from(hashedPassword, 'hex'); - - const cipher = crypto.createCipheriv('aes-256-cbc', aesKey, Buffer.alloc(16)); - - let encryptedBody = cipher.update(hashedPasswordBuffer); - encryptedBody = Buffer.concat([encryptedBody, cipher.final()]); - - response.cookie('ph', encryptedBody.toString('hex'), { domain: '.pretendo.network' }); - - response.redirect(request.redirect || '/account'); }); router.get('/register', async (request, response) => { @@ -136,26 +128,29 @@ router.post('/register', async (request, response) => { response.cookie('username', username, { domain: '.pretendo.network' }); response.cookie('mii_name', mii_name, { domain: '.pretendo.network' }); - const apiResponse = await util.apiPostGetRequest('/v1/register', {}, { - email, username, mii_name, password, password_confirm, hCaptchaResponse - }); + try { + const tokens = await util.register({ + email, + username, + mii_name, + password, + password_confirm, + hCaptchaResponse + }); - if (apiResponse.statusCode !== 200) { - response.cookie('error_message', apiResponse.body.error, { domain: '.pretendo.network' }); + response.cookie('refresh_token', tokens.refresh_token, { domain: '.pretendo.network' }); + response.cookie('access_token', tokens.access_token, { domain: '.pretendo.network' }); + response.cookie('token_type', tokens.token_type, { domain: '.pretendo.network' }); + + response.clearCookie('email', { domain: '.pretendo.network' }); + response.clearCookie('username', { domain: '.pretendo.network' }); + response.clearCookie('mii_name', { domain: '.pretendo.network' }); + + response.redirect(request.redirect || '/account'); + } catch (error) { + response.cookie('error_message', error.message, { domain: '.pretendo.network' }); return response.redirect('/account/register'); } - - const tokens = apiResponse.body; - - response.cookie('refresh_token', tokens.refresh_token, { domain: '.pretendo.network' }); - response.cookie('access_token', tokens.access_token, { domain: '.pretendo.network' }); - response.cookie('token_type', tokens.token_type, { domain: '.pretendo.network' }); - - response.clearCookie('email', { domain: '.pretendo.network' }); - response.clearCookie('username', { domain: '.pretendo.network' }); - response.clearCookie('mii_name', { domain: '.pretendo.network' }); - - response.redirect(request.redirect || '/account'); }); router.get('/logout', async(_request, response) => { @@ -182,57 +177,17 @@ router.get('/connect/discord', pnidMiddleware, async (request, response) => { } // Get Discord user data - const user = await discordOAuth.getUser(tokens.access_token); + const discordUser = await discordOAuth.getUser(tokens.access_token); - // Link the Discord account to the PNID - let apiResponse = await util.apiPostGetRequest('/v1/connections/add/discord', { - 'Authorization': `${request.cookies.token_type} ${request.cookies.access_token}` - }, { - data: { - id: user.id // Only care about user ID. Bot will get details we need - } - }); + try { + await util.updateDiscordConnection(discordUser, request, response); - if (apiResponse.statusCode !== 200) { - // Assume expired, refresh and retry request - apiResponse = await util.apiPostGetRequest('/v1/login', {}, { - refresh_token: request.cookies.refresh_token, - grant_type: 'refresh_token' - }); - - if (apiResponse.statusCode !== 200) { - // TODO: Error message - return response.status(apiResponse.statusCode).json({ - error: 'Bad' - }); - } - - const tokens = apiResponse.body; - - response.cookie('refresh_token', tokens.refresh_token, { domain: '.pretendo.network' }); - response.cookie('access_token', tokens.access_token, { domain: '.pretendo.network' }); - response.cookie('token_type', tokens.token_type, { domain: '.pretendo.network' }); - - apiResponse = await util.apiPostGetRequest('/v1/connections/add/discord', { - 'Authorization': `${tokens.token_type} ${tokens.access_token}` - }, { - data: { - id: user.id - } - }); - - // If still failed, something went horribly wrong - if (apiResponse.statusCode !== 200) { - // TODO: Error message - return response.status(apiResponse.statusCode).json({ - error: 'Bad' - }); - } + response.cookie('success_message', 'Discord account linked successfully', { domain: '.pretendo.network' }); + response.redirect('/account'); + } catch (error) { + response.cookie('error_message', error.message, { domain: '.pretendo.network' }); + return response.redirect('/account'); } - - response.cookie('success_message', 'Discord account linked successfully', { domain: '.pretendo.network' }); - - response.redirect('/account'); }); router.get('/online-files', pnidMiddleware, async (request, response) => { diff --git a/src/util.js b/src/util.js index 0394c6c..d7a561a 100644 --- a/src/util.js +++ b/src/util.js @@ -59,6 +59,86 @@ function apiDeleteGetRequest(path, headers, json) { }); } +async function register(registerData) { + const apiResponse = await apiPostGetRequest('/v1/register', {}, registerData); + + if (apiResponse.statusCode !== 200) { + throw new Error(apiResponse.body.error); + } + + return apiResponse.body; +} + +async function login(username, password) { + const apiResponse = await apiPostGetRequest('/v1/login', {}, { + username, + password, + grant_type: 'password' + }); + + if (apiResponse.statusCode !== 200) { + throw new Error(apiResponse.body.error); + } + + return apiResponse.body; +} + +async function refreshLogin(request, response) { + const apiResponse = await apiPostGetRequest('/v1/login', {}, { + refresh_token: request.cookies.refresh_token, + grant_type: 'refresh_token' + }); + + if (apiResponse.statusCode !== 200) { + // TODO: Error message + throw new Error('Bad'); + } + + const tokens = apiResponse.body; + + response.cookie('refresh_token', tokens.refresh_token, { domain: '.pretendo.network' }); + response.cookie('access_token', tokens.access_token, { domain: '.pretendo.network' }); + response.cookie('token_type', tokens.token_type, { domain: '.pretendo.network' }); +} + +async function getUserAccountData(request, response, fromRetry=false) { + const apiResponse = await apiGetRequest('/v1/user', { + 'Authorization': `${request.cookies.token_type} ${request.cookies.access_token}` + }); + + if (apiResponse.statusCode !== 200 && fromRetry === true) { + // TODO: Error message + throw new Error('Bad'); + } + + if (apiResponse.statusCode !== 200) { + await refreshLogin(request, response); + return await getUserAccountData(request, response, true); + } + + return apiResponse.body; +} + +async function updateDiscordConnection(discordUser, request, response, fromRetry=false) { + const apiResponse = await apiPostGetRequest('/v1/connections/add/discord', { + 'Authorization': `${request.cookies.token_type} ${request.cookies.access_token}` + }, { + data: { + id: discordUser.id + } + }); + + if (apiResponse.statusCode !== 200 && fromRetry === true) { + // TODO: Error message + throw new Error('Bad'); + } + + if (apiResponse.statusCode !== 200) { + await refreshLogin(request, response); + await updateDiscordConnection(discordUser, request, response, true); + } +} + function nintendoPasswordHash(password, pid) { const pidBuffer = Buffer.alloc(4); pidBuffer.writeUInt32LE(pid); @@ -301,6 +381,11 @@ module.exports = { apiGetRequest, apiPostGetRequest, apiDeleteGetRequest, + register, + login, + refreshLogin, + getUserAccountData, + updateDiscordConnection, nintendoPasswordHash, handleStripeEvent, getAccount