Removed ph cookie, users now supply password for online files

This commit is contained in:
Jonathan Barrow 2022-07-16 09:44:28 -04:00
parent ecb07e7b02
commit 12d0a9206e
13 changed files with 170 additions and 35 deletions

View File

@ -34,6 +34,5 @@
"gmail": {
"user": "email@gmail.com",
"pass": "app-password"
},
"aes_key": "hex key here"
}
}

View File

@ -1,3 +1,7 @@
body.modal-open {
overflow: hidden;
}
/* Removing until it's done */
.setting-card a.edit,
.sign-in-history a {
@ -249,6 +253,99 @@ fieldset {
background: #a9375b;
}
div.online-files-modal-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.6);
z-index: 10;
}
div.online-files-modal-wrapper.hidden {
display: none;
}
div.online-files-modal {
background: #393b5f;
padding: 48px;
border-radius: 8px;
text-align: left;
width: min(660px, 90%);
box-sizing: border-box;
}
div.online-files-modal h1 {
margin-top: 0;
}
p.online-files-modal-caption {
color: var(--text-secondary);
}
p.online-files-modal-caption span {
color: var(--text);
}
.online-files-modal-button-wrapper {
margin-top: 24px;
display: flex;
justify-content: flex-end;
}
.online-files-modal-button-wrapper button {
margin-left: 12px;
width: fit-content;
}
.online-files-modal-button-wrapper button.cancel {
background: none;
}
.online-files-modal-button-wrapper button.confirm {
padding: 12px 24px;
}
button {
appearance: none;
-webkit-appearance: none;
display: block;
font-family: Poppins, Arial, Helvetica, sans-serif;
font-size: 1rem;
height: fit-content;
background: var(--btn);
border: none;
border-radius: 4px;
padding: 12px;
color: var(--text);
width: 100%;
transition: filter 300ms;
pointer-events: all;
cursor: pointer;
filter: none;
}
input {
appearance: none;
-webkit-appearance: none;
display: block;
font-family: Poppins, Arial, Helvetica, sans-serif;
font-size: 1rem;
background-color: black; /*change me*/
border: none;
border-radius: 4px;
padding: 12px;
color: var(--text);
width: calc(100% - 24px);
}
input:focus {
background-color: #4b5595;
outline: none;
transition: 150ms;
}
footer {
margin-top: 80px;
}

View File

@ -19,4 +19,44 @@ document.getElementById('remove-discord-connection')?.addEventListener('click',
}
})
.catch(console.log);
});
const onlineFilesModal = document.querySelector('.online-files-modal-wrapper');
const onlineFilesModalButtonConfirm = document.getElementById('onlineFilesConfirmButton');
const onlineFilesModalButtonClose = document.getElementById('onlineFilesCloseButton');
const onlineFilesModalPasswordInput = document.getElementById('password');
document.getElementById('download-cemu-files')?.addEventListener('click', event => {
event.preventDefault();
onlineFilesModal.classList.remove('hidden');
});
onlineFilesModalButtonConfirm?.addEventListener('click', () => {
fetch('/account/online-files', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
password: onlineFilesModalPasswordInput.value
})
})
.then(response => response.blob())
.then()
.then(blob => URL.createObjectURL(blob))
.then(blobUrl => {
const a = document.createElement('a');
a.href = blobUrl;
a.setAttribute('download', 'Cemu Pretendo Online Files.zip');
a.click();
onlineFilesModal.classList.add('hidden');
})
.catch(console.log);
});
onlineFilesModalButtonClose?.addEventListener('click', () => {
onlineFilesModal.classList.add('hidden');
});

View File

@ -3,7 +3,7 @@ const database = require('../database');
async function pnidMiddleware(request, response, next) {
// Verify the user is logged in
if (!request.cookies.access_token || !request.cookies.refresh_token || !request.cookies.ph) {
if (!request.cookies.access_token || !request.cookies.refresh_token) {
return response.redirect(`/account/login?redirect=${request.originalUrl}`);
}

View File

@ -14,7 +14,6 @@ const logger = require('../logger');
const config = require('../../config.json');
const { Router } = express;
const aesKey = Buffer.from(config.aes_key, 'hex');
const stripe = new Stripe(config.stripe.secret_key);
const router = new Router();
@ -48,7 +47,7 @@ router.get('/', pnidMiddleware, async (request, response) => {
renderData.tierLevel = pnid.get('connections.stripe.tier_level');
renderData.account = account;
renderData.isTester = account.access_level > 0;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
// Check if a Discord account is linked to the PNID
if (account.connections.discord.id && account.connections.discord.id.trim() !== '') {
@ -67,7 +66,7 @@ router.get('/', pnidMiddleware, async (request, response) => {
renderData.discordAuthURL = discordAuthURL;
}
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
response.render('account/account', renderData);
});
@ -86,18 +85,6 @@ router.post('/login', async (request, response) => {
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) {
@ -157,7 +144,6 @@ router.get('/logout', async(_request, response) => {
response.clearCookie('refresh_token', { domain: '.pretendo.network' });
response.clearCookie('access_token', { domain: '.pretendo.network' });
response.clearCookie('token_type', { domain: '.pretendo.network' });
response.clearCookie('ph', { domain: '.pretendo.network' });
response.redirect('/');
});
@ -190,13 +176,11 @@ router.get('/connect/discord', pnidMiddleware, async (request, response) => {
}
});
router.get('/online-files', pnidMiddleware, async (request, response) => {
router.post('/online-files', pnidMiddleware, async (request, response) => {
const { account } = request;
const { password } = request.body;
const decipher = crypto.createDecipheriv('aes-256-cbc', aesKey, Buffer.alloc(16));
let decryptedPasswordHash = decipher.update(Buffer.from(request.cookies.ph, 'hex'));
decryptedPasswordHash = Buffer.concat([decryptedPasswordHash, decipher.final()]);
const hashedPassword = util.nintendoPasswordHash(password, account.pid);
const miiNameBuffer = Buffer.alloc(0x16);
const miiName = Buffer.from(account.mii.name, 'utf16le').swap16();
@ -218,7 +202,7 @@ router.get('/online-files', pnidMiddleware, async (request, response) => {
accountDat += 'SimpleAddressId=0\n';
accountDat += `PrincipalId=${account.pid.toString(16)}\n`;
accountDat += 'IsPasswordCacheEnabled=1\n';
accountDat += `AccountPasswordCache=${decryptedPasswordHash.toString('hex')}`;
accountDat += `AccountPasswordCache=${hashedPassword}`;
const onlineFiles = new AdmZip();
@ -227,7 +211,7 @@ router.get('/online-files', pnidMiddleware, async (request, response) => {
onlineFiles.addFile('seeprom.bin', Buffer.alloc(0x200)); // nulled SEEPROM
response.status(200);
response.set('Content-Disposition', 'attachment; filename="Online Files.zip');
response.set('Content-Disposition', 'attachment; filename="Cemu Pretendo Online Files.zip');
response.set('Content-Type', 'application/zip');
response.end(onlineFiles.toBuffer());

View File

@ -6,7 +6,7 @@ router.get('/', async (request, response) => {
const renderData = {};
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
if (renderData.isLoggedIn) {
const account = await util.getAccount(request, response);

View File

@ -40,7 +40,7 @@ router.get('/', async (request, response) => {
postList
};
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
if (renderData.isLoggedIn) {
const account = await util.getAccount(request, response);
@ -74,7 +74,7 @@ router.get('/:slug', async (request, response, next) => {
postList,
};
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
if (renderData.isLoggedIn) {
const account = await util.getAccount(request, response);

View File

@ -15,7 +15,7 @@ router.get('/search', async (request, response) => {
currentPage: request.params.slug
};
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
if (renderData.isLoggedIn) {
const account = await util.getAccount(request, response);
@ -30,7 +30,7 @@ router.get('/:slug', async (request, response, next) => {
currentPage: request.params.slug
};
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
if (renderData.isLoggedIn) {
const account = await util.getAccount(request, response);

View File

@ -11,7 +11,7 @@ router.get('/', async (request, response) => {
boards
};
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
if (renderData.isLoggedIn) {
const account = await util.getAccount(request, response);

View File

@ -5,7 +5,7 @@ const router = new Router();
router.get('/', async (request, response) => {
const renderData = {};
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
if (renderData.isLoggedIn) {
const account = await util.getAccount(request, response);

View File

@ -10,7 +10,7 @@ router.get('/', async (request, response) => {
boards
};
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token && request.cookies.ph;
renderData.isLoggedIn = request.cookies.access_token && request.cookies.refresh_token;
if (renderData.isLoggedIn) {
const account = await util.getAccount(request, response);

View File

@ -20,7 +20,10 @@ const stripe = new Stripe(config.stripe.secret_key);
logger.info('Setting up Middleware');
app.use(morgan('dev'));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.use(cookieParser());
app.use(expressLocale({
'priority': ['cookie', 'accept-language', 'map', 'default'],

View File

@ -172,4 +172,16 @@
</div>
{{/if}}
<div class="online-files-modal-wrapper hidden">
<div class="online-files-modal">
<h1 class="title dot">Password</h1>
<p class="online-files-modal-caption">Enter your PNID password to download Cemu files</p>
<input name="password" id="password" type="password" required>
<div class="online-files-modal-button-wrapper">
<button class="cancel" id="onlineFilesCloseButton">Cancel</button>
<button class="confirm" id="onlineFilesConfirmButton">Confirm</button>
</div>
</div>
</div>
<script src="/assets/js/account.js"></script>