diff --git a/src/services/nnas/routes/support.ts b/src/services/nnas/routes/support.ts index eaf24d9..34ce0cf 100644 --- a/src/services/nnas/routes/support.ts +++ b/src/services/nnas/routes/support.ts @@ -3,8 +3,57 @@ import express from 'express'; import xmlbuilder from 'xmlbuilder'; import moment from 'moment'; import { getPNIDByPID } from '@/database'; +import { Device } from '@/models/device'; import { sendEmailConfirmedEmail, sendConfirmationEmail, sendForgotPasswordEmail } from '@/util'; +// * Middleware to ensure the input device is valid +// TODO - Make this available for more routes? This could be useful elsewhere +async function validateDeviceIDMiddleware(request: express.Request, response: express.Response, next: express.NextFunction) { + const deviceID = request.header('x-nintendo-device-id'); + const serial = request.header('x-nintendo-serial-number'); + + // * Since these values are linked at the time of device creation, and the + // * device ID is always validated against the device certificate for legitimacy + // * we can safely assume that every console hitting our servers through normal + // * means will be stored correctly. And since once both values are set they + // * cannot be changed, these checks will always be safe + const device = await Device.findOne({ + device_id: Number(deviceID), + serial: serial + }); + + if (!device) { + response.status(400).send(xmlbuilder.create({ + errors: { + error: { + cause: 'device_id', + code: '0113', + message: 'Unauthorized device' + } + } + }).end()); + + return; + } + + if (device.access_level < 0) { + response.status(400).send(xmlbuilder.create({ + errors: { + error: { + code: '0012', + message: 'Device has been banned by game server' // TODO - This is not the right error message + } + } + }).end()); + + return; + } + + // TODO - Once we push support for linking PNIDs to consoles, also check if the PID is linked or not + + next(); +} + const router = express.Router(); /** @@ -108,7 +157,7 @@ router.put('/email_confirmation/:pid/:code', async (request: express.Request, re * Replacement for: https://account.nintendo.net/v1/api/support/resend_confirmation * Description: Resends a users confirmation email */ -router.get('/resend_confirmation', async (request: express.Request, response: express.Response): Promise => { +router.get('/resend_confirmation', validateDeviceIDMiddleware, async (request: express.Request, response: express.Response): Promise => { const pid = Number(request.headers['x-nintendo-pid']); const pnid = await getPNIDByPID(pid); @@ -145,19 +194,15 @@ router.get('/resend_confirmation', async (request: express.Request, response: ex * Description: Sends the user a password reset email * NOTE: On NN this was a temp password that expired after 24 hours. We do not do that */ -router.get('/forgotten_password/:pid', async (request: express.Request, response: express.Response): Promise => { - const pid = Number(request.params.pid); - - const pnid = await getPNIDByPID(pid); - - if (!pnid) { - // TODO - Better errors +router.get('/forgotten_password/:pid', validateDeviceIDMiddleware, async (request: express.Request, response: express.Response): Promise => { + if (!/^\d+$/.test(request.params.pid)) { + // * This is what Nintendo sends response.status(400).send(xmlbuilder.create({ errors: { error: { - cause: 'device_id', - code: '0113', - message: 'Unauthorized device' + cause: 'Not Found', + code: '1600', + message: 'Unable to process request' } } }).end()); @@ -165,6 +210,16 @@ router.get('/forgotten_password/:pid', async (request: express.Request, response return; } + const pid = Number(request.params.pid); + const pnid = await getPNIDByPID(pid); + + if (!pnid) { + // * Whenever a PID is a number, but is invalid, Nintendo just 404s + // TODO - When we move to linking PNIDs to consoles, this also applies to valid PIDs not linked to the current console + response.status(404).send(''); + return; + } + await sendForgotPasswordEmail(pnid); response.status(200).send('');