mirror of
https://github.com/PretendoNetwork/account.git
synced 2026-04-25 07:22:47 -05:00
feat: use "enum" for token system type
This commit is contained in:
parent
cad2d0e68e
commit
169e686944
|
|
@ -1,6 +1,7 @@
|
|||
import { Schema, model } from 'mongoose';
|
||||
import uniqueValidator from 'mongoose-unique-validator';
|
||||
import { IServer, IServerMethods, ServerModel } from '@/types/mongoose/server';
|
||||
import type { SystemType } from '@/types/common/token';
|
||||
|
||||
const ServerSchema = new Schema<IServer, ServerModel, IServerMethods>({
|
||||
client_id: String,
|
||||
|
|
@ -18,4 +19,9 @@ const ServerSchema = new Schema<IServer, ServerModel, IServerMethods>({
|
|||
|
||||
ServerSchema.plugin(uniqueValidator, { message: '{PATH} already in use.' });
|
||||
|
||||
export const Server = model<IServer, ServerModel>('Server', ServerSchema);
|
||||
export const Server = model<IServer, ServerModel>('Server', ServerSchema);
|
||||
|
||||
export const serverDeviceToSystemType: Record<number, SystemType> = {
|
||||
1: 'WIIU',
|
||||
2: '3DS'
|
||||
};
|
||||
|
|
@ -109,8 +109,7 @@ router.post('/', async (request: express.Request, response: express.Response): P
|
|||
}
|
||||
|
||||
try {
|
||||
const systemType = 0x3; // * API
|
||||
const tokenGeneration = generateOAuthTokens(systemType, pnid, { refreshExpiresIn: 14 * 24 * 60 * 60 }); // * 14 days
|
||||
const tokenGeneration = generateOAuthTokens('API', pnid, { refreshExpiresIn: 14 * 24 * 60 * 60 }); // * 14 days
|
||||
|
||||
response.json({
|
||||
access_token: tokenGeneration.accessToken,
|
||||
|
|
|
|||
|
|
@ -365,10 +365,9 @@ router.post('/', async (request: express.Request, response: express.Response): P
|
|||
}
|
||||
|
||||
await sendConfirmationEmail(pnid);
|
||||
|
||||
|
||||
try {
|
||||
const systemType = 0x3; // * API
|
||||
const tokenGeneration = generateOAuthTokens(systemType, pnid, { refreshExpiresIn: 14 * 24 * 60 * 60 }); // * 14 days
|
||||
const tokenGeneration = generateOAuthTokens('API', pnid, { refreshExpiresIn: 14 * 24 * 60 * 60 }); // * 14 days
|
||||
|
||||
response.json({
|
||||
access_token: tokenGeneration.accessToken,
|
||||
|
|
|
|||
|
|
@ -54,8 +54,7 @@ export async function login(request: LoginRequest): Promise<DeepPartial<LoginRes
|
|||
}
|
||||
|
||||
try {
|
||||
const systemType = 0x3; // * API
|
||||
const tokenGeneration = generateOAuthTokens(systemType, pnid, { refreshExpiresIn: 14 * 24 * 60 * 60 }); // * 14 days
|
||||
const tokenGeneration = generateOAuthTokens('API', pnid, { refreshExpiresIn: 14 * 24 * 60 * 60 }); // * 14 days
|
||||
|
||||
return {
|
||||
accessToken: tokenGeneration.accessToken,
|
||||
|
|
|
|||
|
|
@ -230,8 +230,7 @@ export async function register(request: RegisterRequest): Promise<DeepPartial<Lo
|
|||
await sendConfirmationEmail(pnid);
|
||||
|
||||
try {
|
||||
const systemType = 0x3; // * API
|
||||
const tokenGeneration = generateOAuthTokens(systemType, pnid, { refreshExpiresIn: 14 * 24 * 60 * 60 }); // * 14 days
|
||||
const tokenGeneration = generateOAuthTokens('API', pnid, { refreshExpiresIn: 14 * 24 * 60 * 60 }); // * 14 days
|
||||
|
||||
return {
|
||||
accessToken: tokenGeneration.accessToken,
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ router.post('/', async (request: express.Request, response: express.Response): P
|
|||
|
||||
async function processLoginRequest(server: HydratedServerDocument, pid: number, titleID: string): Promise<URLSearchParams> {
|
||||
const tokenOptions: TokenOptions = {
|
||||
system_type: 0x2, // * 3DS
|
||||
system_type: '3DS',
|
||||
token_type: 'NEX',
|
||||
pid: pid,
|
||||
access_level: 0,
|
||||
|
|
@ -90,7 +90,7 @@ async function processLoginRequest(server: HydratedServerDocument, pid: number,
|
|||
|
||||
async function processServiceTokenRequest(server: HydratedServerDocument, pid: number, titleID: string): Promise<URLSearchParams> {
|
||||
const tokenOptions: TokenOptions = {
|
||||
system_type: 0x2, // * 3DS
|
||||
system_type: '3DS',
|
||||
token_type: 'SERVICE',
|
||||
pid: pid,
|
||||
access_level: 0,
|
||||
|
|
|
|||
|
|
@ -153,8 +153,7 @@ router.post('/access_token/generate', deviceCertificateMiddleware, consoleStatus
|
|||
}
|
||||
|
||||
try {
|
||||
const systemType = 0x1; // * WiiU
|
||||
const tokenGeneration = generateOAuthTokens(systemType, pnid);
|
||||
const tokenGeneration = generateOAuthTokens('WIIU', pnid);
|
||||
|
||||
response.send(xmlbuilder.create({
|
||||
OAuth20: {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { getServerByClientID, getServerByGameServerID } from '@/database';
|
|||
import { generateToken, getValueFromHeaders, getValueFromQueryString } from '@/util';
|
||||
import { NEXAccount } from '@/models/nex-account';
|
||||
import { TokenOptions } from '@/types/common/token';
|
||||
import { serverDeviceToSystemType } from '@/models/server';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
|
@ -90,8 +91,20 @@ router.get('/service_token/@me', async (request: express.Request, response: expr
|
|||
return;
|
||||
}
|
||||
|
||||
const systemType = serverDeviceToSystemType[server.device];
|
||||
if (!systemType) {
|
||||
response.send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
code: '1021',
|
||||
message: 'The requested game server was not found'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
}
|
||||
|
||||
const tokenOptions: TokenOptions = {
|
||||
system_type: server.device,
|
||||
system_type: systemType,
|
||||
token_type: 'SERVICE',
|
||||
pid: pnid.pid,
|
||||
access_level: pnid.access_level,
|
||||
|
|
@ -213,8 +226,20 @@ router.get('/nex_token/@me', async (request: express.Request, response: express.
|
|||
return;
|
||||
}
|
||||
|
||||
const systemType = serverDeviceToSystemType[server.device];
|
||||
if (!systemType) {
|
||||
response.send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
code: '1021',
|
||||
message: 'The requested game server was not found'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
}
|
||||
|
||||
const tokenOptions: TokenOptions = {
|
||||
system_type: server.device,
|
||||
system_type: systemType,
|
||||
token_type: 'NEX',
|
||||
pid: pnid.pid,
|
||||
access_level: pnid.access_level,
|
||||
|
|
|
|||
|
|
@ -5,15 +5,28 @@ export const TokenTypes = {
|
|||
SERVICE: 4,
|
||||
PASSWORD_RESET: 5
|
||||
} as const;
|
||||
export type TokenType = keyof typeof TokenTypes;
|
||||
|
||||
export function getTokenTypeFromValue(type: number): keyof typeof TokenTypes | undefined {
|
||||
const keys = Object.keys(TokenTypes) as (keyof typeof TokenTypes)[];
|
||||
export function getTokenTypeFromValue(type: number): TokenType | undefined {
|
||||
const keys = Object.keys(TokenTypes) as TokenType[];
|
||||
return keys.find((key) => TokenTypes[key] === type);
|
||||
}
|
||||
|
||||
export const SystemTypes = {
|
||||
'WIIU': 1,
|
||||
'3DS': 2,
|
||||
'API': 3
|
||||
} as const;
|
||||
export type SystemType = keyof typeof SystemTypes;
|
||||
|
||||
export function getSystemTypeFromValue(type: number): SystemType | undefined {
|
||||
const keys = Object.keys(SystemTypes) as SystemType[];
|
||||
return keys.find((key) => SystemTypes[key] === type);
|
||||
}
|
||||
|
||||
export interface Token {
|
||||
system_type: number;
|
||||
token_type: keyof typeof TokenTypes;
|
||||
system_type: SystemType;
|
||||
token_type: TokenType;
|
||||
pid: number;
|
||||
access_level?: number;
|
||||
title_id?: bigint;
|
||||
|
|
|
|||
23
src/util.ts
23
src/util.ts
|
|
@ -10,7 +10,7 @@ import crc32 from 'buffer-crc32';
|
|||
import crc from 'crc';
|
||||
import { sendMail } from '@/mailer';
|
||||
import { config, disabledFeatures } from '@/config-manager';
|
||||
import { getTokenTypeFromValue, OAuthTokenGenerationResponse, OAuthTokenOptions, Token, TokenOptions, TokenTypes } from '@/types/common/token';
|
||||
import { getSystemTypeFromValue, getTokenTypeFromValue, OAuthTokenGenerationResponse, OAuthTokenOptions, SystemType, SystemTypes, Token, TokenOptions, TokenTypes } from '@/types/common/token';
|
||||
import { HydratedPNIDDocument, IPNID, IPNIDMethods } from '@/types/mongoose/pnid';
|
||||
import { SafeQs } from '@/types/common/safe-qs';
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ export function nintendoBase64Encode(decoded: string | Buffer): string {
|
|||
return encoded.replaceAll('+', '.').replaceAll('/', '-').replaceAll('=', '*');
|
||||
}
|
||||
|
||||
export function generateOAuthTokens(systemType: number, pnid: HydratedPNIDDocument, options?: OAuthTokenOptions): OAuthTokenGenerationResponse {
|
||||
export function generateOAuthTokens(systemType: SystemType, pnid: HydratedPNIDDocument, options?: OAuthTokenOptions): OAuthTokenGenerationResponse {
|
||||
const accessTokenExpiresInSecs = options?.accessExpiresIn ?? 60 * 60; // * 1 hour
|
||||
const refreshTokenExpiresInSecs = options?.refreshExpiresIn ?? 24 * 60 * 60; // * 24 hours
|
||||
|
||||
|
|
@ -92,14 +92,15 @@ export function generateOAuthTokens(systemType: number, pnid: HydratedPNIDDocume
|
|||
export function generateToken(key: string, options: TokenOptions): Buffer | null {
|
||||
let dataBuffer = Buffer.alloc(1 + 1 + 4 + 8);
|
||||
|
||||
const systemType = SystemTypes[options.system_type];
|
||||
const tokenType = TokenTypes[options.token_type];
|
||||
|
||||
dataBuffer.writeUInt8(options.system_type, 0x0);
|
||||
dataBuffer.writeUInt8(systemType, 0x0);
|
||||
dataBuffer.writeUInt8(tokenType, 0x1);
|
||||
dataBuffer.writeUInt32LE(options.pid, 0x2);
|
||||
dataBuffer.writeBigUInt64LE(options.expire_time, 0x6);
|
||||
|
||||
if ((options.token_type !== 'OAUTH_ACCESS' && options.token_type !== 'OAUTH_REFRESH') || options.system_type === 0x3) {
|
||||
if ((options.token_type !== 'OAUTH_ACCESS' && options.token_type !== 'OAUTH_REFRESH') || options.system_type === 'API') {
|
||||
// * Access and refresh tokens have smaller bodies due to size constraints
|
||||
// * The API does not have this restraint, however
|
||||
if (options.title_id === undefined || options.access_level === undefined) {
|
||||
|
|
@ -125,7 +126,7 @@ export function generateToken(key: string, options: TokenOptions): Buffer | null
|
|||
|
||||
let final = encrypted;
|
||||
|
||||
if ((options.token_type !== 'OAUTH_ACCESS' && options.token_type !== 'OAUTH_REFRESH') || options.system_type === 0x3) {
|
||||
if ((options.token_type !== 'OAUTH_ACCESS' && options.token_type !== 'OAUTH_REFRESH') || options.system_type === 'API') {
|
||||
// * Access and refresh tokens don't get a checksum due to size constraints
|
||||
const checksum = crc32(dataBuffer);
|
||||
|
||||
|
|
@ -170,15 +171,17 @@ export function decryptToken(token: Buffer, key?: string): Buffer {
|
|||
}
|
||||
|
||||
export function unpackToken(token: Buffer): Token {
|
||||
const systemTypeNum = token.readUInt8(0x0);
|
||||
const tokenTypeNum = token.readUInt8(0x1);
|
||||
|
||||
const systemType = getSystemTypeFromValue(systemTypeNum);
|
||||
const tokenType = getTokenTypeFromValue(tokenTypeNum);
|
||||
|
||||
if (!tokenType) {
|
||||
throw new Error('Invalid token type');
|
||||
}
|
||||
if (!systemType) throw new Error('Invalid system type');
|
||||
if (!tokenType) throw new Error('Invalid token type');
|
||||
|
||||
const unpacked: Token = {
|
||||
system_type: token.readUInt8(0x0),
|
||||
system_type: systemType,
|
||||
token_type: tokenType,
|
||||
pid: token.readUInt32LE(0x2),
|
||||
expire_time: token.readBigUInt64LE(0x6)
|
||||
|
|
@ -271,7 +274,7 @@ export async function sendEmailConfirmedEmail(pnid: mongoose.HydratedDocument<IP
|
|||
|
||||
export async function sendForgotPasswordEmail(pnid: mongoose.HydratedDocument<IPNID, IPNIDMethods>): Promise<void> {
|
||||
const tokenOptions: TokenOptions = {
|
||||
system_type: 0xF, // * API
|
||||
system_type: 'API',
|
||||
token_type: 'PASSWORD_RESET',
|
||||
pid: pnid.pid,
|
||||
access_level: pnid.access_level,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user