diff --git a/README.md b/README.md index 1790c94..335fec5 100644 --- a/README.md +++ b/README.md @@ -230,12 +230,13 @@ Other environment variables may also be used by Node.js, Electron or other packa #### User agent strings -As nxapi can be used in scripts or as a library, it exposes a few different methods for setting a user agent string for requests to the splatnet2statink, flapg, imink and other non-Nintendo APIs. You must include the name and version number of your script/program in the user agent. If your program is not open source or not easily discoverable (e.g. by searching GitHub) it must also include contact information. +As nxapi can be used in scripts or as a library, it exposes a few different methods for setting a user agent string for requests to the imink, flapg and other non-Nintendo APIs. You must include the name and version number of your script/program in the user agent. If your program is not open source or not easily discoverable (e.g. by searching GitHub) it must also include contact information. When using the nxapi command in a script or other program, the `NXAPI_USER_AGENT` environment variable should be used. The `NXAPI_USER_AGENT` environment variable is only used by the nxapi command, and will be ignored by the Electron app or when using nxapi as a library. ```sh -NXAPI_USER_AGENT="your-script/1.0.0 (+https://github.com/...)" nxapi nso ... +export NXAPI_USER_AGENT="your-script/1.0.0 (+https://github.com/...)" +nxapi nso ... ``` When using nxapi as a TypeScript/JavaScript library, the `addUserAgent` function should be used. @@ -331,7 +332,7 @@ The [imink API](https://github.com/JoneWang/imink/wiki/imink-API-Documentation) 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 the [splatnet2statink and flapg APIs](https://github.com/frozenpandaman/splatnet2statink/wiki/api-docs) can be used by setting the `NXAPI_ZNCA_API` environment variable to `flapg`. Note that the splatnet2statink API, which is required to use the flapg API, is deprecated in favour of using only the imink API and scheduled to be shut down by the end of 2022. (`NXAPI_ZNCA_API=flapg nxapi nso ...`) +Alternatively the [flapg API](https://twitter.com/NexusMine/status/1563728086322929665) can be used by setting the `NXAPI_ZNCA_API` environment variable to `flapg`. (`NXAPI_ZNCA_API=flapg nxapi nso ...`) > Since v1.3.0 the default API to use will be fetched from my server and can be changed without an update to nxapi. To force the use of the imink API, set the `NXAPI_ZNCA_API` environment variable to `imink`. diff --git a/docs/cli.md b/docs/cli.md index 463dd8f..c933043 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -201,7 +201,7 @@ curl --no-buffer "http://[::1]:12345/api/znc/presence/events?user=0123456789abcd SplatNet 2 --- -All SplatNet 2 commands may automatically request a web service token. This will involve the splatnet2statink and flapg APIs (or a custom server). This can be disabled by setting `--no-auto-update-session`, however this will cause commands to fail if there isn't a valid SplatNet 2 token. +All SplatNet 2 commands may automatically request a web service token. This will involve the imink/flapg API (or a custom server). This can be disabled by setting `--no-auto-update-session`, however this will cause commands to fail if there isn't a valid SplatNet 2 token. ### User @@ -343,7 +343,7 @@ nxapi nso presence --splatnet2-monitor --sn2-update-interval 60 NookLink --- -All NookLink commands may automatically request a web service token. This will involve the splatnet2statink and flapg APIs (or a custom server). This can be disabled by setting `--no-auto-update-session`, however this will cause commands to fail if there isn't a valid NookLink token. +All NookLink commands may automatically request a web service token. This will involve the imink/flapg API (or a custom server). This can be disabled by setting `--no-auto-update-session`, however this will cause commands to fail if there isn't a valid NookLink token. ### User diff --git a/docs/lib/index.md b/docs/lib/index.md index 30b5d91..dcd1552 100644 --- a/docs/lib/index.md +++ b/docs/lib/index.md @@ -3,7 +3,7 @@ Usage as a JavaScript library nxapi exports it's API library and types. nxapi is split into several modules that can be imported separately. -> You must set a user agent string using the `addUserAgent` function when using anything that contacts non-Nintendo APIs, such as the splatnet2statink API. +> You must set a user agent string using the `addUserAgent` function when using anything that contacts non-Nintendo APIs, such as the imink API. > Please read https://github.com/frozenpandaman/splatnet2statink/wiki/api-docs and https://github.com/JoneWang/imink/wiki/imink-API-Documentation if you intend to share anything you create. diff --git a/resources/common/remote-config.json b/resources/common/remote-config.json index 5467cb9..6ebaf7d 100644 --- a/resources/common/remote-config.json +++ b/resources/common/remote-config.json @@ -5,7 +5,7 @@ }, "coral_auth": { "default": "imink", - "splatnet2statink": {}, + "splatnet2statink": null, "flapg": {}, "imink": {} }, diff --git a/src/api/f.ts b/src/api/f.ts index 6240d0b..a3f24dd 100644 --- a/src/api/f.ts +++ b/src/api/f.ts @@ -20,9 +20,10 @@ export abstract class ZncaApi { } // -// splatnet2statink + flapg +// flapg // +/** @deprecated The flapg API no longer requires client authentication */ export async function getLoginHash(token: string, timestamp: string | number, useragent?: string) { const { default: { coral_auth: { splatnet2statink: config } } } = await import('../common/remote-config.js'); if (!config) throw new Error('Remote configuration prevents splatnet2statink API use'); @@ -66,43 +67,47 @@ export interface LoginHashApiError { } export async function flapg( - token: string, timestamp: string | number, guid: string, iid: FlapgIid, + hash_method: '1' | '2', token: string, + timestamp?: string | number, request_id?: string, useragent?: string ) { const { default: { coral_auth: { flapg: config } } } = await import('../common/remote-config.js'); if (!config) throw new Error('Remote configuration prevents flapg API use'); - const hash = await getLoginHash(token, timestamp, useragent); - debugFlapg('Getting f parameter', { - token, timestamp, guid, iid, + hash_method, token, timestamp, request_id, }); + const req: FlapgApiRequest = { + hash_method, + token, + timestamp: typeof timestamp === 'number' ? '' + timestamp : undefined, + request_id, + }; + const [signal, cancel] = timeoutSignal(); - const response = await fetch('https://flapg.com/ika2/api/login?public', { + const response = await fetch('https://flapg.com/ika/api/login-main', { + method: 'POST', headers: { + 'Content-Type': 'application/json', 'User-Agent': getUserAgent(useragent), - 'x-token': token, - 'x-time': '' + timestamp, - 'x-guid': guid, - 'x-hash': hash, - 'x-ver': '3', - 'x-iid': iid, }, + body: JSON.stringify(req), signal, }).finally(cancel); if (response.status !== 200) { - throw new ErrorResponse('[flapg] Non-200 status code', response, await response.text()); + throw new ErrorResponse('[flapg] Non-200 status code', response, await response.text()); } const data = await response.json() as FlapgApiResponse; - debugFlapg('Got f parameter "%s"', data.result.f); + debugFlapg('Got f parameter', data); return data; } +/** @deprecated */ export enum FlapgIid { /** Nintendo Switch Online app token */ NSO = 'nso', @@ -110,31 +115,26 @@ export enum FlapgIid { APP = 'app', } -export interface FlapgApiResponse { - result: { - f: string; - p1: string; - p2: string; - p3: string; - }; -} +export type FlapgApiRequest = IminkFRequest; +export type FlapgApiResponse = IminkFResponse; +export type FlapgApiError = IminkFError; export class ZncaApiFlapg extends ZncaApi { + /** @deprecated */ async getLoginHash(id_token: string, timestamp: string) { return getLoginHash(id_token, timestamp, this.useragent); } async genf(token: string, hash_method: '1' | '2') { - const timestamp = Date.now(); const request_id = uuidgen(); - const type = hash_method === '2' ? FlapgIid.APP : FlapgIid.NSO; - const result = await flapg(token, timestamp, request_id, type, this.useragent); + const result = await flapg(hash_method, token, undefined, request_id, this.useragent); return { provider: 'flapg' as const, - token, timestamp, request_id, hash_method, type, - f: result.result.f, + hash_method, token, request_id, + timestamp: result.timestamp, + f: result.f, result, }; } @@ -153,7 +153,7 @@ export async function iminkf( if (!config) throw new Error('Remote configuration prevents imink API use'); debugImink('Getting f parameter', { - token, request_id, hash_method, + hash_method, token, timestamp, request_id, }); const req: IminkFRequest = { @@ -175,13 +175,13 @@ export async function iminkf( }).finally(cancel); if (response.status !== 200) { - throw new ErrorResponse('[imink] Non-200 status code', response, await response.text()); + throw new ErrorResponse('[imink] Non-200 status code', response, await response.text()); } const data = await response.json() as IminkFResponse | IminkFError; if ('error' in data) { - throw new ErrorResponse('[imink] ' + data.reason, response, data); + throw new ErrorResponse('[imink] ' + data.reason, response, data); } debugImink('Got f parameter "%s"', data.f); @@ -231,7 +231,7 @@ export async function genf( useragent?: string ) { debugZncaApi('Getting f parameter', { - url, token, timestamp, request_id, + url, hash_method, token, timestamp, request_id, }); const req: AndroidZncaFRequest = { @@ -253,14 +253,14 @@ export async function genf( }).finally(cancel); if (response.status !== 200) { - throw new ErrorResponse('[znca-api] Non-200 status code', response, await response.text()); + throw new ErrorResponse('[znca-api] Non-200 status code', response, await response.text()); } const data = await response.json() as AndroidZncaFResponse | AndroidZncaFError; 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, response, data); } debugZncaApi('Got f parameter', data, response.headers); @@ -320,7 +320,6 @@ export type FResult = { result: unknown; } & ({ provider: 'flapg'; - type: FlapgIid; result: FlapgApiResponse; } | { provider: 'imink';