Add an option to use the imink API to generate f parameters

https://github.com/JoneWang/imink/wiki/imink-API-Documentation
https://github.com/JoneWang
This commit is contained in:
Samuel Elliott
2022-05-26 12:41:50 +01:00
parent 967a063220
commit 442b53a062
4 changed files with 185 additions and 63 deletions

View File

@@ -5,8 +5,13 @@ import { version } from '../util/product.js';
const debugS2s = createDebug('nxapi:api:s2s');
const debugFlapg = createDebug('nxapi:api:flapg');
const debugImink = createDebug('nxapi:api:imink');
const debugZncaApi = createDebug('nxapi:api:znca-api');
//
// splatnet2statink + flapg
//
export async function getLoginHash(token: string, timestamp: string | number, useragent?: string) {
debugS2s('Getting login hash');
@@ -33,6 +38,13 @@ export async function getLoginHash(token: string, timestamp: string | number, us
return data.hash;
}
export interface LoginHashApiResponse {
hash: string;
}
export interface LoginHashApiError {
error: string;
}
export async function flapg(
token: string, timestamp: string | number, guid: string, iid: FlapgIid,
useragent?: string
@@ -59,9 +71,82 @@ export async function flapg(
debugFlapg('Got f parameter "%s"', data.result.f);
return data.result;
return data;
}
export enum FlapgIid {
/** Nintendo Switch Online app token */
NSO = 'nso',
/** Web service token */
APP = 'app',
}
export interface FlapgApiResponse {
result: {
f: string;
p1: string;
p2: string;
p3: string;
};
}
//
// imink
//
export async function iminkf(
token: string, timestamp: string | number, uuid: string, hash_method: '1' | '2',
useragent?: string
) {
debugImink('Getting f parameter', {
token, timestamp, uuid, hash_method,
});
const req: IminkFRequest = {
hash_method,
token,
timestamp: '' + timestamp,
request_id: uuid,
};
const response = await fetch('https://api.imink.app/f', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': (useragent ? useragent + ' ' : '') + 'nxapi/' + version,
},
body: JSON.stringify(req),
});
const data = await response.json() as IminkFResponse | IminkFError;
if ('error' in data) {
throw new ErrorResponse('[imink] ' + data.reason, response, data);
}
debugImink('Got f parameter "%s"', data.f);
return data;
}
export interface IminkFRequest {
timestamp: string;
request_id: string;
hash_method: '1' | '2';
token: string;
}
export interface IminkFResponse {
f: string;
}
export interface IminkFError {
reason: string;
error: true;
}
//
// nxapi znca API server
//
export async function genf(
url: string, token: string, timestamp: string | number, uuid: string, type: FlapgIid,
useragent?: string
@@ -95,46 +180,7 @@ export async function genf(
debugZncaApi('Got f parameter "%s"', data.f);
return data.f;
}
export async function genfc(
url: string, token: string, timestamp: string | number, uuid: string, type: FlapgIid,
useragent?: string
) {
const f = await genf(url, token, timestamp, uuid, type, useragent);
return {
f,
p1: token,
p2: '' + timestamp,
p3: uuid,
url,
};
}
export interface LoginHashApiResponse {
hash: string;
}
export interface LoginHashApiError {
error: string;
}
export enum FlapgIid {
/** Nintendo Switch Online app token */
NSO = 'nso',
/** Web service token */
APP = 'app',
}
export interface FlapgApiResponse {
result: {
f: string;
p1: string;
p2: string;
p3: string;
};
return data;
}
export interface AndroidZncaFRequest {
@@ -149,3 +195,80 @@ export interface AndroidZncaFResponse {
export interface AndroidZncaFError {
error: string;
}
export async function f(
token: string, timestamp: string | number, uuid: string, type: FlapgIid,
useragent?: string,
provider: FApi = getEnvApi()
): Promise<FResult> {
if (provider === 'flapg') {
const result = await flapg(token, timestamp, uuid, type, useragent);
return {
provider: 'flapg',
token, timestamp: '' + timestamp, uuid, type,
f: result.result.f, result,
};
}
if (provider === 'imink') {
const result = await iminkf(token, timestamp, uuid, type === FlapgIid.APP ? '2' : '1', useragent);
return {
provider: 'imink',
token, timestamp: '' + timestamp, uuid, type,
f: result.f, result,
};
}
if (provider[0] === 'nxapi') {
const result = await genf(provider[1], token, timestamp, uuid, type, useragent);
return {
provider: 'nxapi', url: provider[1],
token, timestamp: '' + timestamp, uuid, type,
f: result.f, result,
};
}
throw new Error('Unknown znca API provider');
}
export type FApi =
'flapg' |
'imink' |
['nxapi', string];
export type FResult = {
provider: string;
token: string;
timestamp: string;
uuid: string;
type: FlapgIid;
f: string;
result: unknown;
} & ({
provider: 'flapg';
result: FlapgApiResponse;
} | {
provider: 'imink';
result: IminkFResponse;
} | {
provider: 'nxapi';
url: string;
result: AndroidZncaFResponse;
});
function getEnvApi(): FApi {
if (process.env.NXAPI_ZNCA_API) {
if (process.env.NXAPI_ZNCA_API === 'flapg') {
return 'flapg';
}
if (process.env.NXAPI_ZNCA_API === 'imink') {
return 'imink';
}
throw new Error('Unknown znca API provider');
}
if (process.env.ZNCA_API_URL?.startsWith('https://')) {
return ['nxapi', process.env.ZNCA_API_URL + '/f'];
}
return 'flapg';
}

View File

@@ -1,7 +1,7 @@
import fetch from 'node-fetch';
import { v4 as uuidgen } from 'uuid';
import createDebug from 'debug';
import { flapg, FlapgIid, genfc } from './f.js';
import { f, FlapgIid } from './f.js';
import { AccountLogin, AccountToken, Announcements, CurrentUser, CurrentUserPermissions, Event, Friends, GetActiveEventResult, PresencePermissions, User, WebServices, WebServiceToken, ZncResponse, ZncStatus } from './znc-types.js';
import { getNintendoAccountToken, getNintendoAccountUser, NintendoAccountUser } from './na.js';
import { ErrorResponse } from './util.js';
@@ -126,9 +126,7 @@ export default class ZncApi {
const timestamp = '' + Math.floor(Date.now() / 1000);
const useragent = this.useragent ?? undefined;
const data = process.env.ZNCA_API_URL ?
await genfc(process.env.ZNCA_API_URL + '/f', this.token, timestamp, uuid, FlapgIid.APP, useragent) :
await flapg(this.token, timestamp, uuid, FlapgIid.APP, useragent);
const data = await f(this.token, timestamp, uuid, FlapgIid.APP, useragent);
const req = {
id,
@@ -150,14 +148,12 @@ export default class ZncApi {
const id_token = nintendoAccountToken.id_token;
const useragent = this.useragent ?? undefined;
const flapgdata = process.env.ZNCA_API_URL ?
await genfc(process.env.ZNCA_API_URL + '/f', id_token, timestamp, uuid, FlapgIid.NSO, useragent) :
await flapg(id_token, timestamp, uuid, FlapgIid.NSO, useragent);
const fdata = await f(id_token, timestamp, uuid, FlapgIid.NSO, useragent);
const req = {
naBirthday: user.birthday,
timestamp,
f: flapgdata.f,
f: fdata.f,
requestId: uuid,
naIdToken: id_token,
};
@@ -169,7 +165,7 @@ export default class ZncApi {
timestamp,
nintendoAccountToken,
// user,
flapg: flapgdata,
f: fdata,
nsoAccount: data.result,
credential: data.result.webApiServerCredential,
};
@@ -203,9 +199,7 @@ export default class ZncApi {
const user = await getNintendoAccountUser(nintendoAccountToken);
const id_token = nintendoAccountToken.id_token;
const flapgdata = process.env.ZNCA_API_URL ?
await genfc(process.env.ZNCA_API_URL + '/f', id_token, timestamp, uuid, FlapgIid.NSO, useragent ?? undefined) :
await flapg(id_token, timestamp, uuid, FlapgIid.NSO, useragent ?? undefined);
const fdata = await f(id_token, timestamp, uuid, FlapgIid.NSO, useragent ?? undefined);
debug('Getting Nintendo Switch Online app token');
@@ -225,11 +219,12 @@ export default class ZncApi {
language: user.language,
timestamp,
requestId: uuid,
f: flapgdata.f,
f: fdata.f,
},
}),
});
debug('fetch %s %s, response %s', 'POST', '/v3/Account/Login', response.status);
const data = await response.json() as ZncResponse<AccountLogin>;
if ('errorMessage' in data) {
@@ -246,7 +241,7 @@ export default class ZncApi {
timestamp,
nintendoAccountToken,
user,
flapg: flapgdata,
f: fdata,
nsoAccount: data.result,
credential: data.result.webApiServerCredential,
};