account/src/services/api/routes/v1/login.js
2022-09-30 21:24:14 -04:00

129 lines
3.3 KiB
JavaScript

const router = require('express').Router();
const bcrypt = require('bcrypt');
const fs = require('fs-extra');
const database = require('../../../../database');
const cache = require('../../../../cache');
const util = require('../../../../util');
/**
* [POST]
* Implementation of for: https://api.pretendo.cc/v1/login
* Description: Generates an access token for an API user
* TODO: Replace this with a more robust OAuth2 implementation
*/
router.post('/', async (request, response) => {
const { body } = request;
const { grant_type, username, password, refresh_token } = body;
if (!['password', 'refresh_token'].includes(grant_type)) {
return response.status(400).json({
app: 'api',
status: 400,
error: 'Invalid grant type'
});
}
if (grant_type === 'password' && (!username || username.trim() === '')) {
return response.status(400).json({
app: 'api',
status: 400,
error: 'Invalid or missing username'
});
}
if (grant_type === 'password' && (!password || password.trim() === '')) {
return response.status(400).json({
app: 'api',
status: 400,
error: 'Invalid or missing password'
});
}
if (grant_type === 'refresh_token' && (!refresh_token || refresh_token.trim() === '')) {
return response.status(400).json({
app: 'api',
status: 400,
error: 'Invalid or missing refresh token'
});
}
let pnid;
if (grant_type === 'password') {
pnid = await database.getUserByUsername(username);
if (!pnid) {
return response.status(400).json({
app: 'api',
status: 400,
error: 'User not found'
});
}
const hashedPassword = util.nintendoPasswordHash(password, pnid.get('pid'));
if (!pnid || !bcrypt.compareSync(hashedPassword, pnid.password)) {
return response.status(400).json({
app: 'api',
status: 400,
error: 'Invalid or missing password'
});
}
} else {
pnid = await database.getUserBearer(refresh_token);
if (!pnid) {
return response.status(400).json({
app: 'api',
status: 400,
error: 'Invalid or missing refresh token'
});
}
}
const cryptoPath = `${__dirname}/../../../../../certs/service/account`;
if (!await fs.pathExists(cryptoPath)) {
// Need to generate keys
return response.status(500).json({
app: 'api',
status: 500,
error: 'Failed to locate crypto keys. Please contact an administrator'
});
}
const publicKey = await cache.getServicePublicKey('account');
const secretKey = await cache.getServiceSecretKey('account');
const cryptoOptions = {
public_key: publicKey,
hmac_secret: secretKey
};
const accessTokenOptions = {
system_type: 0xF, // API
token_type: 0x1, // OAuth Access,
pid: pnid.get('pid'),
access_level: pnid.get('access_level'),
title_id: BigInt(0),
expire_time: BigInt(Date.now() + (3600 * 1000))
};
const refreshTokenOptions = {
system_type: 0xF, // API
token_type: 0x2, // OAuth Refresh,
pid: pnid.get('pid'),
access_level: pnid.get('access_level'),
title_id: BigInt(0),
expire_time: BigInt(Date.now() + (3600 * 1000))
};
const accessToken = await util.generateToken(cryptoOptions, accessTokenOptions);
const refreshToken = await util.generateToken(cryptoOptions, refreshTokenOptions);
response.json({
access_token: accessToken,
token_type: 'Bearer',
expires_in: 3600,
refresh_token: refreshToken
});
});
module.exports = router;