mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-03-21 18:04:10 -05:00
Prevent attempting to authenticate more than four times per hour
This only affects scripts - running this nxapi command in a terminal and the Electron app will ignore this limit. https://github.com/samuelthomas2774/nxapi/issues/9
This commit is contained in:
parent
d3e5a88625
commit
e12bb364a7
|
|
@ -758,7 +758,7 @@ nxapi exports it's API library and types. [See src/exports.](src/exports)
|
|||
|
||||
Example authenticating to the Nintendo Switch Online app:
|
||||
|
||||
> This is a simplified example of authenticating to the Coral API and using cached tokens. More logic is required to ensure you are using these APIs properly - [see src/common/auth/nso.ts for the authentication functions used in nxapi's CLI and Electron app](src/common/auth/nso.ts).
|
||||
> This is a simplified example of authenticating to the Coral API and using cached tokens. More logic is required to ensure you are using these APIs properly - [see src/common/auth/coral.ts for the authentication functions used in nxapi's CLI and Electron app](src/common/auth/coral.ts).
|
||||
|
||||
```ts
|
||||
import { addUserAgent } from 'nxapi';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { ActiveEvent, Announcements, CurrentUser, Event, Friend, Presence, Prese
|
|||
import { ErrorResponse } from './util.js';
|
||||
import CoralApi from './coral.js';
|
||||
import { NintendoAccountUser } from './na.js';
|
||||
import { SavedToken } from '../common/auth/nso.js';
|
||||
import { SavedToken } from '../common/auth/coral.js';
|
||||
import { timeoutSignal } from '../util/misc.js';
|
||||
import { getAdditionalUserAgents, getUserAgent } from '../util/useragent.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { ErrorResponse } from '../../api/util.js';
|
|||
import { DiscordPresence } from '../../discord/util.js';
|
||||
import ipc, { events } from './ipc.js';
|
||||
import { NintendoAccountUser } from '../../api/na.js';
|
||||
import { SavedToken } from '../../common/auth/nso.js';
|
||||
import { SavedToken } from '../../common/auth/coral.js';
|
||||
import { SavedMoonToken } from '../../common/auth/moon.js';
|
||||
import { BACKGROUND_COLOUR_MAIN_DARK, BACKGROUND_COLOUR_MAIN_LIGHT, DEFAULT_ACCENT_COLOUR } from './constants.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -175,7 +175,8 @@ export class Store extends EventEmitter {
|
|||
) {
|
||||
super();
|
||||
|
||||
this.users = Users.coral(storage, process.env.ZNC_PROXY_URL);
|
||||
// ratelimit = false, as most users.get calls are triggered by user interaction (or at startup)
|
||||
this.users = Users.coral(storage, process.env.ZNC_PROXY_URL, false);
|
||||
}
|
||||
|
||||
async saveMonitorState(monitors: PresenceMonitorManager) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { askAddNsoAccount, askAddPctlAccount } from './na-auth.js';
|
|||
import { App } from './index.js';
|
||||
import { WebService } from '../../api/coral-types.js';
|
||||
import openWebService from './webservices.js';
|
||||
import { SavedToken } from '../../common/auth/nso.js';
|
||||
import { SavedToken } from '../../common/auth/coral.js';
|
||||
import { SavedMoonToken } from '../../common/auth/moon.js';
|
||||
import { dev, dir } from '../../util/product.js';
|
||||
import { EmbeddedPresenceMonitor, EmbeddedProxyPresenceMonitor } from './monitor.js';
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { BrowserWindow, dialog, MessageBoxOptions, Notification, session, shell
|
|||
import { getNintendoAccountSessionToken, NintendoAccountSessionToken } from '../../api/na.js';
|
||||
import { ZNCA_CLIENT_ID } from '../../api/coral.js';
|
||||
import { ZNMA_CLIENT_ID } from '../../api/moon.js';
|
||||
import { getToken, SavedToken } from '../../common/auth/nso.js';
|
||||
import { getToken, SavedToken } from '../../common/auth/coral.js';
|
||||
import { getPctlToken, SavedMoonToken } from '../../common/auth/moon.js';
|
||||
import { Jwt } from '../../util/jwt.js';
|
||||
import { tryGetNativeImageFromUrl } from './util.js';
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { dev } from '../../util/product.js';
|
|||
import { WebService } from '../../api/coral-types.js';
|
||||
import { Store } from './index.js';
|
||||
import type { NativeShareRequest, NativeShareUrlRequest } from '../preload-webservice/znca-js-api.js';
|
||||
import { SavedToken } from '../../common/auth/nso.js';
|
||||
import { SavedToken } from '../../common/auth/coral.js';
|
||||
import { createWebServiceWindow } from './windows.js';
|
||||
|
||||
const debug = createDebug('app:main:webservices');
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import createDebug from 'debug';
|
|||
import type { User } from 'discord-rpc';
|
||||
import type { SharingItem } from '../main/electron.js';
|
||||
import type { DiscordPresenceConfiguration, DiscordPresenceSource, WindowConfiguration } from '../common/types.js';
|
||||
import type { SavedToken } from '../../common/auth/nso.js';
|
||||
import type { SavedToken } from '../../common/auth/coral.js';
|
||||
import type { SavedMoonToken } from '../../common/auth/moon.js';
|
||||
import type { UpdateCacheData } from '../../common/update.js';
|
||||
import type { Announcements, CurrentUser, Friend, GetActiveEventResult, WebService, WebServices } from '../../api/coral-types.js';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import createDebug from 'debug';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
|
||||
const debug = createDebug('cli:nso:add-friend');
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Table from '../util/table.js';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
|
||||
const debug = createDebug('cli:nso:announcements');
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import createDebug from 'debug';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
import { getNintendoAccountSessionToken } from '../../api/na.js';
|
||||
import { ZNCA_CLIENT_ID } from '../../api/coral.js';
|
||||
import prompt from '../util/prompt.js';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import createDebug from 'debug';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
|
||||
const debug = createDebug('cli:nso:friendcode');
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import type { Arguments as ParentArguments } from '../nso.js';
|
|||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { hrduration } from '../../util/misc.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
|
||||
const debug = createDebug('cli:nso:friends');
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import CoralApi from '../../api/coral.js';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken, SavedToken } from '../../common/auth/nso.js';
|
||||
import { getToken, SavedToken } from '../../common/auth/coral.js';
|
||||
import { NotificationManager, PresenceEvent, ZncNotifications } from '../../common/notify.js';
|
||||
import { product } from '../../util/product.js';
|
||||
import { parseListenAddress } from '../../util/net.js';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import createDebug from 'debug';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
|
||||
const debug = createDebug('cli:nso:lookup');
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import persist from 'node-persist';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
import { EmbeddedSplatNet2Monitor, NotificationManager, ZncNotifications } from '../../common/notify.js';
|
||||
import { CurrentUser, Friend, Game } from '../../api/coral-types.js';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { PresencePermissions } from '../../api/coral-types.js';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
|
||||
const debug = createDebug('cli:nso:permissions');
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import createDebug from 'debug';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
import { DiscordPresencePlayTime } from '../../discord/util.js';
|
||||
import { handleEnableSplatNet2Monitoring, TerminalNotificationManager } from './notify.js';
|
||||
import { ZncDiscordPresence, ZncProxyDiscordPresence } from '../../common/presence.js';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import createDebug from 'debug';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
import prompt from '../util/prompt.js';
|
||||
|
||||
const debug = createDebug('cli:nso:token');
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import createDebug from 'debug';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
|
||||
const debug = createDebug('cli:nso:user');
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Table from '../util/table.js';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
|
||||
const debug = createDebug('cli:nso:webservices');
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import createDebug from 'debug';
|
|||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
|
||||
const debug = createDebug('cli:nso:webservicetoken');
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import createDebug from 'debug';
|
|||
import fetch from 'node-fetch';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
import { AuthPolicy, AuthToken } from '../../api/znc-proxy.js';
|
||||
import { Argv } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Table from './util/table.js';
|
|||
import type { Arguments as ParentArguments } from '../cli.js';
|
||||
import { Argv } from '../util/yargs.js';
|
||||
import { initStorage } from '../util/storage.js';
|
||||
import { SavedToken } from '../common/auth/nso.js';
|
||||
import { SavedToken } from '../common/auth/coral.js';
|
||||
import { SavedMoonToken } from '../common/auth/moon.js';
|
||||
|
||||
const debug = createDebug('cli:users');
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import type { Arguments as ParentArguments } from '../util.js';
|
|||
import { DiscordPresenceContext, DiscordPresencePlayTime, getDiscordPresence, getInactiveDiscordPresence } from '../../discord/util.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getToken } from '../../common/auth/coral.js';
|
||||
import { timeoutSignal } from '../../util/misc.js';
|
||||
import { getUserAgent } from '../../util/useragent.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { Jwt } from '../../util/jwt.js';
|
|||
import { AccountLogin, CoralErrorResponse } from '../../api/coral-types.js';
|
||||
import CoralApi, { ZNCA_CLIENT_ID } from '../../api/coral.js';
|
||||
import ZncProxyApi from '../../api/znc-proxy.js';
|
||||
import { checkUseLimit, SHOULD_LIMIT_USE } from './util.js';
|
||||
|
||||
const debug = createDebug('nxapi:auth:nso');
|
||||
|
||||
|
|
@ -24,15 +25,21 @@ export interface SavedToken {
|
|||
proxy_url?: string;
|
||||
}
|
||||
|
||||
export async function getToken(storage: persist.LocalStorage, token: string, proxy_url: string): Promise<{
|
||||
export async function getToken(
|
||||
storage: persist.LocalStorage, token: string, proxy_url: string, ratelimit?: boolean
|
||||
): Promise<{
|
||||
nso: ZncProxyApi;
|
||||
data: SavedToken;
|
||||
}>
|
||||
export async function getToken(storage: persist.LocalStorage, token: string, proxy_url?: string): Promise<{
|
||||
export async function getToken(
|
||||
storage: persist.LocalStorage, token: string, proxy_url?: string, ratelimit?: boolean
|
||||
): Promise<{
|
||||
nso: CoralApi;
|
||||
data: SavedToken;
|
||||
}>
|
||||
export async function getToken(storage: persist.LocalStorage, token: string, proxy_url?: string) {
|
||||
export async function getToken(
|
||||
storage: persist.LocalStorage, token: string, proxy_url?: string, ratelimit = SHOULD_LIMIT_USE
|
||||
) {
|
||||
if (!token) {
|
||||
console.error('No token set. Set a Nintendo Account session token using the `--token` option or by running `nxapi nso token`.');
|
||||
throw new Error('Invalid token');
|
||||
|
|
@ -58,6 +65,8 @@ export async function getToken(storage: persist.LocalStorage, token: string, pro
|
|||
const existingToken: SavedToken | undefined = await storage.getItem('NsoToken.' + token);
|
||||
|
||||
if (!existingToken || existingToken.expires_at <= Date.now()) {
|
||||
if (ratelimit) await checkUseLimit(storage, 'coral', jwt.payload.sub);
|
||||
|
||||
console.warn('Authenticating to Nintendo Switch Online app');
|
||||
debug('Authenticating to znc with session token');
|
||||
|
||||
|
|
@ -4,6 +4,7 @@ import { ZNMA_CLIENT_ID } from '../../api/moon.js';
|
|||
import { NintendoAccountSessionTokenJwtPayload, NintendoAccountToken, NintendoAccountUser } from '../../api/na.js';
|
||||
import { Jwt } from '../../util/jwt.js';
|
||||
import MoonApi from '../../api/moon.js';
|
||||
import { checkUseLimit, SHOULD_LIMIT_USE } from './util.js';
|
||||
|
||||
const debug = createDebug('nxapi:auth:moon');
|
||||
|
||||
|
|
@ -14,7 +15,7 @@ export interface SavedMoonToken {
|
|||
expires_at: number;
|
||||
}
|
||||
|
||||
export async function getPctlToken(storage: persist.LocalStorage, token: string) {
|
||||
export async function getPctlToken(storage: persist.LocalStorage, token: string, ratelimit = SHOULD_LIMIT_USE) {
|
||||
if (!token) {
|
||||
console.error('No token set. Set a Nintendo Account session token using the `--token` option or by running `nxapi pctl auth`.');
|
||||
throw new Error('Invalid token');
|
||||
|
|
@ -40,6 +41,8 @@ export async function getPctlToken(storage: persist.LocalStorage, token: string)
|
|||
const existingToken: SavedMoonToken | undefined = await storage.getItem('MoonToken.' + token);
|
||||
|
||||
if (!existingToken || existingToken.expires_at <= Date.now()) {
|
||||
if (ratelimit) await checkUseLimit(storage, 'moon', jwt.payload.sub);
|
||||
|
||||
console.warn('Authenticating to Nintendo Switch Parental Controls app');
|
||||
debug('Authenticating to pctl with session token');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import createDebug from 'debug';
|
||||
import persist from 'node-persist';
|
||||
import { getToken } from './nso.js';
|
||||
import { getToken } from './coral.js';
|
||||
import NooklinkApi, { NooklinkUserApi } from '../../api/nooklink.js';
|
||||
import { AuthToken, Users } from '../../api/nooklink-types.js';
|
||||
import { WebServiceToken } from '../../api/coral-types.js';
|
||||
import { checkUseLimit, SHOULD_LIMIT_USE } from './util.js';
|
||||
import { Jwt } from '../../util/jwt.js';
|
||||
import { NintendoAccountSessionTokenJwtPayload } from '../../api/na.js';
|
||||
|
||||
const debug = createDebug('nxapi:auth:nooklink');
|
||||
|
||||
|
|
@ -19,7 +22,8 @@ export interface SavedToken {
|
|||
}
|
||||
|
||||
export async function getWebServiceToken(
|
||||
storage: persist.LocalStorage, token: string, proxy_url?: string, allow_fetch_token = false
|
||||
storage: persist.LocalStorage, token: string, proxy_url?: string,
|
||||
allow_fetch_token = false, ratelimit = SHOULD_LIMIT_USE
|
||||
) {
|
||||
if (!token) {
|
||||
console.error('No token set. Set a Nintendo Account session token using the `--token` option or by running `nxapi nso token`.');
|
||||
|
|
@ -30,7 +34,12 @@ export async function getWebServiceToken(
|
|||
|
||||
if (!existingToken || existingToken.expires_at <= Date.now()) {
|
||||
if (!allow_fetch_token) {
|
||||
throw new Error('No valid _gtoken cookie');
|
||||
throw new Error('No valid NookLink web service token');
|
||||
}
|
||||
|
||||
if (ratelimit) {
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(token);
|
||||
await checkUseLimit(storage, 'nooklink', jwt.payload.sub);
|
||||
}
|
||||
|
||||
console.warn('Authenticating to NookLink');
|
||||
|
|
@ -66,7 +75,7 @@ type PromiseValue<T> = T extends PromiseLike<infer R> ? R : never;
|
|||
|
||||
export async function getUserToken(
|
||||
storage: persist.LocalStorage, nintendoAccountToken: string, user?: string,
|
||||
proxy_url?: string, allow_fetch_token = false
|
||||
proxy_url?: string, allow_fetch_token = false, ratelimit = SHOULD_LIMIT_USE
|
||||
) {
|
||||
let wst: PromiseValue<ReturnType<typeof getWebServiceToken>> | null = null;
|
||||
|
||||
|
|
@ -106,6 +115,11 @@ export async function getUserToken(
|
|||
if (!wst) wst = await getWebServiceToken(storage, nintendoAccountToken, proxy_url, allow_fetch_token);
|
||||
const {nooklink, data: webserviceToken} = wst;
|
||||
|
||||
if (ratelimit) {
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(nintendoAccountToken);
|
||||
await checkUseLimit(storage, 'nooklink-user', jwt.payload.sub);
|
||||
}
|
||||
|
||||
console.warn('Authenticating to NookLink as user %s', user);
|
||||
debug('Authenticating to NookLink as user %s', user);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ import process from 'node:process';
|
|||
import * as fs from 'node:fs';
|
||||
import createDebug from 'debug';
|
||||
import persist from 'node-persist';
|
||||
import { getToken } from './nso.js';
|
||||
import { getToken } from './coral.js';
|
||||
import SplatNet2Api, { updateIksmSessionLastUsed } from '../../api/splatnet2.js';
|
||||
import { WebServiceToken } from '../../api/coral-types.js';
|
||||
import { checkUseLimit, SHOULD_LIMIT_USE } from './util.js';
|
||||
import { Jwt } from '../../util/jwt.js';
|
||||
import { NintendoAccountSessionTokenJwtPayload } from '../../api/na.js';
|
||||
|
||||
const debug = createDebug('nxapi:auth:splatnet2');
|
||||
|
||||
|
|
@ -27,7 +30,10 @@ export interface SavedIksmSessionToken {
|
|||
last_used?: number;
|
||||
}
|
||||
|
||||
export async function getIksmToken(storage: persist.LocalStorage, token: string, proxy_url?: string, allow_fetch_token = false) {
|
||||
export async function getIksmToken(
|
||||
storage: persist.LocalStorage, token: string, proxy_url?: string,
|
||||
allow_fetch_token = false, ratelimit = SHOULD_LIMIT_USE
|
||||
) {
|
||||
if (!token) {
|
||||
console.error('No token set. Set a Nintendo Account session token using the `--token` option or by running `nxapi nso token`.');
|
||||
throw new Error('Invalid token');
|
||||
|
|
@ -44,6 +50,11 @@ export async function getIksmToken(storage: persist.LocalStorage, token: string,
|
|||
throw new Error('No valid iksm_session cookie');
|
||||
}
|
||||
|
||||
if (ratelimit) {
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(token);
|
||||
await checkUseLimit(storage, 'splatnet2', jwt.payload.sub);
|
||||
}
|
||||
|
||||
console.warn('Authenticating to SplatNet 2');
|
||||
debug('Authenticating to SplatNet 2');
|
||||
|
||||
|
|
|
|||
30
src/common/auth/util.ts
Normal file
30
src/common/auth/util.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import createDebug from 'debug';
|
||||
import * as persist from 'node-persist';
|
||||
|
||||
const debug = createDebug('nxapi:auth:util');
|
||||
|
||||
// If the parent process is a terminal, then the user is attempting to run the command manually,
|
||||
// so we shouldn't restrict how many attempts they can use. If not, the command is being run by
|
||||
// a script/some other program, which should be limited in case it continues to run the command
|
||||
// if it fails. The Electron app overrides this as the parent process (probably) won't be a
|
||||
// terminal, but most attempts to call getToken won't be automated.
|
||||
export const SHOULD_LIMIT_USE = !process.stdout.isTTY;
|
||||
const LIMIT_REQUESTS = 4;
|
||||
const LIMIT_PERIOD = 60 * 60 * 1000; // 60 minutes
|
||||
|
||||
type RateLimitAttempts = number[];
|
||||
|
||||
export async function checkUseLimit(
|
||||
storage: persist.LocalStorage,
|
||||
key: string, user: string
|
||||
) {
|
||||
let attempts: RateLimitAttempts = await storage.getItem('RateLimitAttempts-' + key + '.' + user) ?? [];
|
||||
attempts = attempts.filter(a => a >= Date.now() - LIMIT_PERIOD);
|
||||
|
||||
if (attempts.length >= LIMIT_REQUESTS) {
|
||||
throw new Error('Too many attempts to authenticate');
|
||||
}
|
||||
|
||||
attempts.unshift(Date.now());
|
||||
await storage.setItem('RateLimitAttempts-' + key + '.' + user, attempts);
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import CoralApi from '../api/coral.js';
|
|||
import { ActiveEvent, Announcements, CurrentUser, Friend, Game, Presence, PresenceState, WebServices, CoralErrorResponse } from '../api/coral-types.js';
|
||||
import ZncProxyApi from '../api/znc-proxy.js';
|
||||
import { ErrorResponse } from '../api/util.js';
|
||||
import { SavedToken } from './auth/nso.js';
|
||||
import { SavedToken } from './auth/coral.js';
|
||||
import { SplatNet2RecordsMonitor } from './splatnet2/monitor.js';
|
||||
import Loop, { LoopResult } from '../util/loop.js';
|
||||
import { getTitleIdFromEcUrl, hrduration } from '../util/misc.js';
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@ import * as persist from 'node-persist';
|
|||
import CoralApi from '../api/coral.js';
|
||||
import ZncProxyApi from '../api/znc-proxy.js';
|
||||
import { Announcements, Friends, GetActiveEventResult, WebServices, CoralSuccessResponse } from '../api/coral-types.js';
|
||||
import { getToken, SavedToken } from './auth/nso.js';
|
||||
import { Jwt } from '../util/jwt.js';
|
||||
import { NintendoAccountSessionTokenJwtPayload } from '../api/na.js';
|
||||
import { getToken, SavedToken } from './auth/coral.js';
|
||||
|
||||
const debug = createDebug('nxapi:users');
|
||||
|
||||
|
|
@ -24,11 +22,6 @@ export default class Users<T extends UserData> {
|
|||
}
|
||||
|
||||
async get(token: string): Promise<T> {
|
||||
if (debug.enabled) {
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(token);
|
||||
debug('Getting user for token', jwt.payload.sub);
|
||||
}
|
||||
|
||||
const existing = this.users.get(token);
|
||||
|
||||
if (existing && existing.expires_at >= Date.now()) {
|
||||
|
|
@ -47,11 +40,11 @@ export default class Users<T extends UserData> {
|
|||
return promise;
|
||||
}
|
||||
|
||||
static coral(storage: persist.LocalStorage, znc_proxy_url: string): Users<CoralUser<ZncProxyApi>>
|
||||
static coral(storage: persist.LocalStorage, znc_proxy_url?: string): Users<CoralUser>
|
||||
static coral(storage: persist.LocalStorage, znc_proxy_url?: string) {
|
||||
static coral(storage: persist.LocalStorage, znc_proxy_url: string, ratelimit?: boolean): Users<CoralUser<ZncProxyApi>>
|
||||
static coral(storage: persist.LocalStorage, znc_proxy_url?: string, ratelimit?: boolean): Users<CoralUser>
|
||||
static coral(storage: persist.LocalStorage, znc_proxy_url?: string, ratelimit?: boolean) {
|
||||
return new Users(async token => {
|
||||
const {nso, data} = await getToken(storage, token, znc_proxy_url);
|
||||
const {nso, data} = await getToken(storage, token, znc_proxy_url, ratelimit);
|
||||
|
||||
const [announcements, friends, webservices, active_event] = await Promise.all([
|
||||
nso.getAnnouncements(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user