mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-07-17 16:58:24 -05:00
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:
parent
967a063220
commit
442b53a062
15
README.md
15
README.md
|
|
@ -9,7 +9,8 @@ Access the Nintendo Switch Online and Nintendo Switch Parental Controls app APIs
|
|||
- Interactive Nintendo Account login for the Nintendo Switch Online and Nintendo Switch Parental Controls apps
|
||||
- Automated login to the Nintendo Switch Online app API
|
||||
- This uses splatnet2statink and flapg APIs by default.
|
||||
- Alternatively a custom server using a rooted Android device/emulator is included.
|
||||
- Alternatively the imink API or a custom server can be used.
|
||||
- A custom server using a rooted Android device/emulator is included.
|
||||
- Get Nintendo Switch account information, friends list and game-specific services
|
||||
- Show Discord Rich Presence using your own or a friend's Nintendo Switch presence
|
||||
- Show your account's friend code (or a custom friend code)
|
||||
|
|
@ -313,7 +314,9 @@ The splatnet2statink and flapg APIs are used by default to automate authenticati
|
|||
|
||||
Specifically, the tokens sent are JSON Web Tokens. The token sent to login to the app includes [this information and is valid for 15 minutes](https://gitlab.fancy.org.uk/samuel/nxapi/-/wikis/Nintendo-tokens#nintendo-account-id_token), and the token sent to login to web services includes [this information and is valid for two hours](https://gitlab.fancy.org.uk/samuel/nxapi/-/wikis/Nintendo-tokens#nintendo-switch-online-app-token).
|
||||
|
||||
Alternatively nxapi includes a custom server using Frida on an Android device/emulator that can be used instead of these.
|
||||
Alternatively the [imink API](https://github.com/JoneWang/imink/wiki/imink-API-Documentation) can be used by setting the `NXAPI_ZNCA_API` environment variable to `imink`. (`NXAPI_ZNCA_API=imink nxapi nso ...`)
|
||||
|
||||
nxapi also includes a custom server using Frida on an Android device/emulator that can be used instead of these.
|
||||
|
||||
This is only required for Nintendo Switch Online app data. Nintendo Switch Parental Controls data can be fetched without sending an access token to a third-party API.
|
||||
|
||||
|
|
@ -477,7 +480,7 @@ nxapi nooklink island
|
|||
# Use a specific NookLink user linked to the selected Nintendo Account
|
||||
# If more than 1 NookLink users exist by default the first user will be used
|
||||
nxapi nooklink user --islander 0x0123456789abcdef
|
||||
# --user can also be used to selecte a different Nintendo Account
|
||||
# --user can also be used to select a different Nintendo Account
|
||||
nxapi nooklink user --user 0123456789abcdef
|
||||
nxapi nooklink user --user 0123456789abcdef --islander 0x0123456789abcdef
|
||||
```
|
||||
|
|
@ -639,14 +642,14 @@ NXAPI_DATA_PATH=`pwd`/data nxapi ...
|
|||
|
||||
#### Debug logs
|
||||
|
||||
Logging uses the `debug` package and can be controlled using the `DEBUG` environment variable. All nxapi logging uses the `api` and `cli` namespaces.
|
||||
Logging uses the `debug` package and can be controlled using the `DEBUG` environment variable. All nxapi logging uses the `nxapi` and `cli` namespaces.
|
||||
|
||||
```sh
|
||||
# Show all debug logs from nxapi
|
||||
DEBUG=api,api:*,cli,cli:* nxapi ...
|
||||
DEBUG=nxapi:*,cli,cli:* nxapi ...
|
||||
|
||||
# Show all API requests
|
||||
DEBUG=api:* nxapi ...
|
||||
DEBUG=nxapi:api:* nxapi ...
|
||||
|
||||
# Show all debug logs
|
||||
DEBUG=* nxapi ...
|
||||
|
|
|
|||
205
src/api/f.ts
205
src/api/f.ts
|
|
@ -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';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import createDebug from 'debug';
|
||||
import * as persist from 'node-persist';
|
||||
import { FlapgApiResponse } from '../../api/f.js';
|
||||
import { FlapgApiResponse, FResult } from '../../api/f.js';
|
||||
import { NintendoAccountSessionTokenJwtPayload, NintendoAccountToken, NintendoAccountUser } from '../../api/na.js';
|
||||
import { Jwt } from '../../util/jwt.js';
|
||||
import { AccountLogin } from '../../api/znc-types.js';
|
||||
|
|
@ -14,7 +14,8 @@ export interface SavedToken {
|
|||
timestamp: string;
|
||||
nintendoAccountToken: NintendoAccountToken;
|
||||
user: NintendoAccountUser;
|
||||
flapg: FlapgApiResponse['result'];
|
||||
f: FResult;
|
||||
flapg?: FlapgApiResponse['result'];
|
||||
nsoAccount: AccountLogin;
|
||||
credential: AccountLogin['webApiServerCredential'];
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user