diff --git a/src/api/coral.ts b/src/api/coral.ts index cb6f621..9e77340 100644 --- a/src/api/coral.ts +++ b/src/api/coral.ts @@ -1,7 +1,7 @@ import fetch, { Response } from 'node-fetch'; import { v4 as uuidgen } from 'uuid'; import createDebug from 'debug'; -import { f, FResult } from './f.js'; +import { f, FResult, HashMethod } from './f.js'; import { AccountLogin, AccountToken, Announcements, CurrentUser, CurrentUserPermissions, Event, Friends, GetActiveEventResult, PresencePermissions, User, WebServices, WebServiceToken, CoralErrorResponse, CoralResponse, CoralStatus, CoralSuccessResponse, FriendCodeUser, FriendCodeUrl, AccountTokenParameter, AccountLoginParameter } from './coral-types.js'; import { getNintendoAccountToken, getNintendoAccountUser, NintendoAccountToken, NintendoAccountUser } from './na.js'; import { ErrorResponse } from './util.js'; @@ -178,7 +178,7 @@ export default class CoralApi { } async getWebServiceToken(id: string) { - const data = await f(this.token, '2', this.useragent ?? getAdditionalUserAgents()); + const data = await f(this.token, HashMethod.WEB_SERVICE, this.useragent ?? getAdditionalUserAgents()); const req = { id, @@ -195,7 +195,8 @@ export default class CoralApi { // Nintendo Account token const nintendoAccountToken = await getNintendoAccountToken(token, ZNCA_CLIENT_ID); - const fdata = await f(nintendoAccountToken.id_token, '1', this.useragent ?? getAdditionalUserAgents()); + const fdata = await f(nintendoAccountToken.id_token, HashMethod.CORAL, + this.useragent ?? getAdditionalUserAgents()); const req: AccountTokenParameter = { naBirthday: user.birthday, @@ -250,7 +251,7 @@ export default class CoralApi { // Nintendo Account user data const user = await getNintendoAccountUser(nintendoAccountToken); - const fdata = await f(nintendoAccountToken.id_token, '1', useragent); + const fdata = await f(nintendoAccountToken.id_token, HashMethod.CORAL, useragent); debug('Getting Nintendo Switch Online app token'); diff --git a/src/api/f.ts b/src/api/f.ts index a3f24dd..6bbad87 100644 --- a/src/api/f.ts +++ b/src/api/f.ts @@ -16,7 +16,12 @@ export abstract class ZncaApi { public useragent?: string ) {} - abstract genf(token: string, hash_method: '1' | '2'): Promise; + abstract genf(token: string, hash_method: HashMethod): Promise; +} + +export enum HashMethod { + CORAL = 1, + WEB_SERVICE = 2, } // @@ -67,7 +72,7 @@ export interface LoginHashApiError { } export async function flapg( - hash_method: '1' | '2', token: string, + hash_method: HashMethod, token: string, timestamp?: string | number, request_id?: string, useragent?: string ) { @@ -79,7 +84,7 @@ export async function flapg( }); const req: FlapgApiRequest = { - hash_method, + hash_method: '' + hash_method as `${HashMethod}`, token, timestamp: typeof timestamp === 'number' ? '' + timestamp : undefined, request_id, @@ -115,7 +120,13 @@ export enum FlapgIid { APP = 'app', } -export type FlapgApiRequest = IminkFRequest; +export interface FlapgApiRequest { + hash_method: '1' | '2'; + token: string; + timestamp?: string; + request_id?: string; +} + export type FlapgApiResponse = IminkFResponse; export type FlapgApiError = IminkFError; @@ -125,7 +136,7 @@ export class ZncaApiFlapg extends ZncaApi { return getLoginHash(id_token, timestamp, this.useragent); } - async genf(token: string, hash_method: '1' | '2') { + async genf(token: string, hash_method: HashMethod) { const request_id = uuidgen(); const result = await flapg(hash_method, token, undefined, request_id, this.useragent); @@ -145,7 +156,7 @@ export class ZncaApiFlapg extends ZncaApi { // export async function iminkf( - hash_method: '1' | '2', token: string, + hash_method: HashMethod, token: string, timestamp?: number, request_id?: string, useragent?: string ) { @@ -190,9 +201,9 @@ export async function iminkf( } export interface IminkFRequest { - hash_method: '1' | '2'; + hash_method: 1 | 2 | '1' | '2'; token: string; - timestamp?: string; + timestamp?: string | number; request_id?: string; } export interface IminkFResponse { @@ -206,7 +217,7 @@ export interface IminkFError { } export class ZncaApiImink extends ZncaApi { - async genf(token: string, hash_method: '1' | '2') { + async genf(token: string, hash_method: HashMethod) { const request_id = uuidgen(); const result = await iminkf(hash_method, token, undefined, request_id, this.useragent); @@ -226,7 +237,7 @@ export class ZncaApiImink extends ZncaApi { // export async function genf( - url: string, hash_method: '1' | '2', + url: string, hash_method: HashMethod, token: string, timestamp?: number, request_id?: string, useragent?: string ) { @@ -235,7 +246,7 @@ export async function genf( }); const req: AndroidZncaFRequest = { - hash_method, + hash_method: '' + hash_method as `${HashMethod}`, token, timestamp, request_id, @@ -260,7 +271,7 @@ export async function genf( if ('error' in data) { debugZncaApi('Error getting f parameter "%s"', data.error); - throw new ErrorResponse('[znca-api] ' + data.error, response, data); + throw new ErrorResponse('[znca-api] ' + data.error_message ?? data.error, response, data); } debugZncaApi('Got f parameter', data, response.headers); @@ -281,6 +292,7 @@ export interface AndroidZncaFResponse { } export interface AndroidZncaFError { error: string; + error_message?: string; } export class ZncaApiNxapi extends ZncaApi { @@ -288,7 +300,7 @@ export class ZncaApiNxapi extends ZncaApi { super(useragent); } - async genf(token: string, hash_method: '1' | '2') { + async genf(token: string, hash_method: HashMethod) { const request_id = uuidgen(); const result = await genf(this.url + '/f', hash_method, token, undefined, request_id, this.useragent); @@ -304,7 +316,9 @@ export class ZncaApiNxapi extends ZncaApi { } } -export async function f(token: string, hash_method: '1' | '2', useragent?: string): Promise { +export async function f(token: string, hash_method: HashMethod | `${HashMethod}`, useragent?: string): Promise { + if (typeof hash_method === 'string') hash_method = parseInt(hash_method); + const provider = getPreferredZncaApiFromEnvironment(useragent) ?? await getDefaultZncaApi(useragent); return provider.genf(token, hash_method); @@ -312,7 +326,7 @@ export async function f(token: string, hash_method: '1' | '2', useragent?: strin export type FResult = { provider: string; - hash_method: '1' | '2'; + hash_method: HashMethod; token: string; timestamp: number; request_id: string; diff --git a/src/cli/android-znca-api-server-frida.ts b/src/cli/android-znca-api-server-frida.ts index f05a0bc..82877f4 100644 --- a/src/cli/android-znca-api-server-frida.ts +++ b/src/cli/android-znca-api-server-frida.ts @@ -183,7 +183,7 @@ export async function handler(argv: ArgumentsCamelCase) { await ready; let data: { - hash_method: '1' | '2'; + hash_method: '1' | '2' | 1 | 2; token: string; timestamp?: string | number; request_id?: string; @@ -203,6 +203,9 @@ export async function handler(argv: ArgumentsCamelCase) { request_id: data.uuid, }; + if (data && data.hash_method === 1) data.hash_method = '1'; + if (data && data.hash_method === 2) data.hash_method = '2'; + if ( !data || typeof data !== 'object' || diff --git a/src/exports/coral.ts b/src/exports/coral.ts index 23cb1a0..2c40244 100644 --- a/src/exports/coral.ts +++ b/src/exports/coral.ts @@ -10,6 +10,7 @@ export { default as ZncProxyApi } from '../api/znc-proxy.js'; export { ZncaApi, + HashMethod, getPreferredZncaApiFromEnvironment, getDefaultZncaApi, f,