mirror of
https://github.com/PretendoNetwork/account.git
synced 2026-08-19 17:26:37 -05:00
feat: give service tokens state
This commit is contained in:
@@ -5,7 +5,7 @@ import type { Model, HydratedDocument } from 'mongoose';
|
||||
export interface IIndependentServiceToken {
|
||||
token: string;
|
||||
client_id: string;
|
||||
title_ids: string[];
|
||||
title_id: string;
|
||||
pid: number;
|
||||
info: {
|
||||
system_type: number;
|
||||
@@ -27,7 +27,7 @@ export type HydratedIndependentServiceTokenDocument = HydratedDocument<IIndepend
|
||||
const IndependentServiceTokenSchema = new Schema<IIndependentServiceToken, IndependentServiceTokenModel, IIndependentServiceTokenMethods>({
|
||||
token: String,
|
||||
client_id: String,
|
||||
title_ids: [String],
|
||||
title_id: String,
|
||||
pid: Number,
|
||||
info: {
|
||||
system_type: Number,
|
||||
|
||||
@@ -2,7 +2,7 @@ import crypto from 'node:crypto';
|
||||
import express from 'express';
|
||||
import { SystemType } from '@/types/common/system-types';
|
||||
import { TokenType } from '@/types/common/token-types';
|
||||
import { nintendoBase64Encode, nintendoBase64Decode, nascDateTime, nascError } from '@/util';
|
||||
import { nintendoBase64Encode, nintendoBase64Decode, nascDateTime, nascError, createServiceToken } from '@/util';
|
||||
import { getServerByTitleID } from '@/database';
|
||||
import { IndependentServiceToken } from '@/models/independent_service_token';
|
||||
import { NEXToken } from '@/models/nex_token';
|
||||
@@ -96,18 +96,25 @@ async function processLoginRequest(server: HydratedServerDocument, pid: number,
|
||||
});
|
||||
}
|
||||
|
||||
async function processServiceTokenRequest(_server: HydratedServerDocument, pid: number, titleID: string): Promise<URLSearchParams> {
|
||||
const serviceToken = await IndependentServiceToken.create({
|
||||
token: crypto.randomBytes(36).toString('base64'),
|
||||
client_id: '', // * Unused in NASC
|
||||
title_ids: [titleID],
|
||||
async function processServiceTokenRequest(server: HydratedServerDocument, pid: number, titleID: string): Promise<URLSearchParams> {
|
||||
const serviceTokenOptions = {
|
||||
pid: pid,
|
||||
title_id: titleID,
|
||||
issued: new Date(),
|
||||
expires: new Date(Date.now() + 24 * 3600 * 1000)
|
||||
};
|
||||
|
||||
const serviceToken = await IndependentServiceToken.create({
|
||||
token: nintendoBase64Encode(createServiceToken(server, serviceTokenOptions)),
|
||||
client_id: server.game_server_id,
|
||||
title_id: serviceTokenOptions.title_id,
|
||||
pid: serviceTokenOptions.pid,
|
||||
info: {
|
||||
system_type: SystemType.CTR,
|
||||
token_type: TokenType.IndependentService,
|
||||
title_id: BigInt(parseInt(titleID, 16)),
|
||||
issued: new Date(),
|
||||
expires: new Date(Date.now() + 12 * 3600 * 1000) // * These don't technicaly expire, thats up to the independent server, but adding this as a suggested expiration anyway
|
||||
issued: serviceTokenOptions.issued,
|
||||
expires: serviceTokenOptions.expires
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import crypto from 'node:crypto';
|
||||
import express from 'express';
|
||||
import xmlbuilder from 'xmlbuilder';
|
||||
import { getServerByClientID, getServerByGameServerID } from '@/database';
|
||||
import { getValueFromHeaders, getValueFromQueryString } from '@/util';
|
||||
import { createServiceToken, getValueFromHeaders, getValueFromQueryString } from '@/util';
|
||||
import { TokenType } from '@/types/common/token-types';
|
||||
import { IndependentServiceToken } from '@/models/independent_service_token';
|
||||
import { NEXToken } from '@/models/nex_token';
|
||||
@@ -93,17 +93,24 @@ router.get('/service_token/@me', async (request: express.Request, response: expr
|
||||
return;
|
||||
}
|
||||
|
||||
const serviceToken = await IndependentServiceToken.create({
|
||||
token: crypto.randomBytes(36).toString('base64'),
|
||||
client_id: clientID,
|
||||
title_ids: [titleID],
|
||||
const serviceTokenOptions = {
|
||||
pid: pnid.pid,
|
||||
title_id: titleID,
|
||||
issued: new Date(),
|
||||
expires: new Date(Date.now() + 24 * 3600 * 1000)
|
||||
};
|
||||
|
||||
const serviceToken = await IndependentServiceToken.create({
|
||||
token: createServiceToken(server, serviceTokenOptions).toString('base64'),
|
||||
client_id: clientID,
|
||||
title_id: serviceTokenOptions.title_id,
|
||||
pid: serviceTokenOptions.pid,
|
||||
info: {
|
||||
system_type: server.device,
|
||||
token_type: TokenType.IndependentService,
|
||||
title_id: BigInt(parseInt(titleID, 16)),
|
||||
issued: new Date(),
|
||||
expires: new Date(Date.now() + 12 * 3600 * 1000) // * These don't technicaly expire, thats up to the independent server, but adding this as a suggested expiration anyway
|
||||
issued: serviceTokenOptions.issued,
|
||||
expires: serviceTokenOptions.expires
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
6
src/types/common/service-token-options.ts
Normal file
6
src/types/common/service-token-options.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface ServiceTokenOptions {
|
||||
pid: number;
|
||||
title_id: string;
|
||||
issued: Date;
|
||||
expires: Date;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { SystemType } from '@/types/common/system-types';
|
||||
import type { TokenType } from '@/types/common/token-types';
|
||||
|
||||
export interface TokenOptions {
|
||||
system_type: SystemType;
|
||||
token_type: TokenType;
|
||||
pid: number;
|
||||
access_level?: number;
|
||||
title_id?: bigint;
|
||||
expire_time: bigint;
|
||||
}
|
||||
20
src/util.ts
20
src/util.ts
@@ -14,6 +14,8 @@ import type { ObjectCannedACL } from '@aws-sdk/client-s3';
|
||||
import type { IncomingHttpHeaders } from 'node:http';
|
||||
import type { IPNID, IPNIDMethods } from '@/types/mongoose/pnid';
|
||||
import type { SafeQs } from '@/types/common/safe-qs';
|
||||
import type { HydratedServerDocument } from '@/types/mongoose/server';
|
||||
import type { ServiceTokenOptions } from '@/types/common/service-token-options';
|
||||
|
||||
let s3: S3;
|
||||
|
||||
@@ -53,6 +55,24 @@ export function nintendoBase64Encode(decoded: string | Buffer): string {
|
||||
return encoded.replaceAll('+', '.').replaceAll('/', '-').replaceAll('=', '*');
|
||||
}
|
||||
|
||||
export function createServiceToken(server: HydratedServerDocument, options: ServiceTokenOptions): Buffer {
|
||||
const dataBuffer = Buffer.alloc(28);
|
||||
|
||||
dataBuffer.writeUInt32BE(options.pid, 0);
|
||||
dataBuffer.writeBigUInt64BE(BigInt(parseInt(options.title_id, 16)), 4);
|
||||
dataBuffer.writeBigUInt64BE(BigInt(options.issued.getTime()), 12);
|
||||
dataBuffer.writeBigUInt64BE(BigInt(options.expires.getTime()), 20);
|
||||
|
||||
// * Not using AES anymore but fuck it, it's here already
|
||||
// TODO - rename the AES field
|
||||
const hmac = crypto.createHmac('sha256', server.aes_key).update(dataBuffer).digest();
|
||||
|
||||
return Buffer.concat([
|
||||
dataBuffer,
|
||||
hmac
|
||||
]);
|
||||
}
|
||||
|
||||
export function fullUrl(request: express.Request): string {
|
||||
const protocol = request.protocol;
|
||||
const host = request.host;
|
||||
|
||||
Reference in New Issue
Block a user