mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-10-06 01:27:37 -05:00
Move authentication functions
app no longer imports from cli
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import createDebug from 'debug';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import createDebug from 'debug';
|
||||
import mkdirp from 'mkdirp';
|
||||
import type { Arguments as ParentArguments } from '../nooklink.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getUserToken } from './util.js';
|
||||
import { getUserToken } from '../../common/auth/nooklink.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink:dump-newspapers');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../nooklink.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getUserToken, getWebServiceToken } from './util.js';
|
||||
import { getUserToken, getWebServiceToken } from '../../common/auth/nooklink.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink:island');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { promisify } from 'util';
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../nooklink.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getUserToken } from './util.js';
|
||||
import { getUserToken } from '../../common/auth/nooklink.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink:keyboard');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../nooklink.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getUserToken } from './util.js';
|
||||
import { getUserToken } from '../../common/auth/nooklink.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink:newspaper');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../nooklink.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getUserToken } from './util.js';
|
||||
import { getUserToken } from '../../common/auth/nooklink.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink:newspapers');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../nooklink.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getUserToken } from './util.js';
|
||||
import { getUserToken } from '../../common/auth/nooklink.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink:post-reaction');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../nooklink.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getUserToken } from './util.js';
|
||||
import { getUserToken } from '../../common/auth/nooklink.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink:reactions');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../nooklink.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getUserToken } from './util.js';
|
||||
import { getUserToken } from '../../common/auth/nooklink.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink:user');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../nooklink.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getWebServiceToken } from './util.js';
|
||||
import { getWebServiceToken } from '../../common/auth/nooklink.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink:users');
|
||||
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
import createDebug from 'debug';
|
||||
import persist from 'node-persist';
|
||||
import { getToken } from '../nso/util.js';
|
||||
import NooklinkApi, { NooklinkUserApi } from '../../api/nooklink.js';
|
||||
import { AuthToken, Users } from '../../api/nooklink-types.js';
|
||||
import { WebServiceToken } from '../../api/znc-types.js';
|
||||
|
||||
const debug = createDebug('cli:nooklink');
|
||||
|
||||
export interface SavedToken {
|
||||
webserviceToken: WebServiceToken;
|
||||
url: string;
|
||||
cookies: string;
|
||||
body: string;
|
||||
|
||||
gtoken: string;
|
||||
expires_at: number;
|
||||
useragent: string;
|
||||
}
|
||||
|
||||
export async function getWebServiceToken(
|
||||
storage: persist.LocalStorage, token: string, proxy_url?: string, allow_fetch_token = false
|
||||
) {
|
||||
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');
|
||||
}
|
||||
|
||||
const existingToken: SavedToken | undefined = await storage.getItem('NookToken.' + token);
|
||||
|
||||
if (!existingToken || existingToken.expires_at <= Date.now()) {
|
||||
if (!allow_fetch_token) {
|
||||
throw new Error('No valid _gtoken cookie');
|
||||
}
|
||||
|
||||
console.warn('Authenticating to NookLink');
|
||||
debug('Authenticating to NookLink');
|
||||
|
||||
const {nso, data} = await getToken(storage, token, proxy_url);
|
||||
|
||||
const existingToken: SavedToken = await NooklinkApi.loginWithZnc(nso, data.user);
|
||||
|
||||
await storage.setItem('NookToken.' + token, existingToken);
|
||||
|
||||
return {
|
||||
nooklink: new NooklinkApi(existingToken.gtoken, existingToken.useragent),
|
||||
data: existingToken,
|
||||
};
|
||||
}
|
||||
|
||||
debug('Using existing web service token');
|
||||
|
||||
return {
|
||||
nooklink: new NooklinkApi(existingToken.gtoken, existingToken.useragent),
|
||||
data: existingToken,
|
||||
};
|
||||
}
|
||||
|
||||
export interface SavedUserToken {
|
||||
token: AuthToken;
|
||||
user: string;
|
||||
webserviceToken: SavedToken;
|
||||
}
|
||||
|
||||
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
|
||||
) {
|
||||
let wst: PromiseValue<ReturnType<typeof getWebServiceToken>> | null = null;
|
||||
|
||||
if (!user) {
|
||||
let cachedUsers: {
|
||||
users: Users;
|
||||
expires_at: number;
|
||||
} | undefined = await storage.getItem('NookUsers.' + nintendoAccountToken);
|
||||
|
||||
if (!cachedUsers || cachedUsers.expires_at <= Date.now()) {
|
||||
if (!wst) wst = await getWebServiceToken(storage, nintendoAccountToken, proxy_url, allow_fetch_token);
|
||||
const {nooklink, data: webserviceToken} = wst;
|
||||
|
||||
debug('Fetching users');
|
||||
const users = await nooklink.getUsers();
|
||||
|
||||
await storage.setItem('NookUsers.' + nintendoAccountToken, cachedUsers = {
|
||||
users,
|
||||
expires_at: webserviceToken.expires_at,
|
||||
});
|
||||
}
|
||||
|
||||
if (!cachedUsers.users.users.length) {
|
||||
throw new Error('No Animal Crossing: New Horizons save data linked to NookLink');
|
||||
}
|
||||
|
||||
if (cachedUsers.users.users.length > 1) {
|
||||
console.warn('More than 1 NookLink user is linked to this Nintendo Account. The first player will be used. Use `--islander` to set a specific user.');
|
||||
}
|
||||
|
||||
user = cachedUsers.users.users[0].id;
|
||||
}
|
||||
|
||||
const existingToken: SavedUserToken | undefined = await storage.getItem('NookAuthToken.' + nintendoAccountToken + '.' + user);
|
||||
|
||||
if (!existingToken || existingToken.token.expireAt <= (Date.now() / 1000)) {
|
||||
if (!wst) wst = await getWebServiceToken(storage, nintendoAccountToken, proxy_url, allow_fetch_token);
|
||||
const {nooklink, data: webserviceToken} = wst;
|
||||
|
||||
console.warn('Authenticating to NookLink as user %s', user);
|
||||
debug('Authenticating to NookLink as user %s', user);
|
||||
|
||||
const token = await nooklink.getAuthToken(user);
|
||||
|
||||
const existingToken: SavedUserToken = {
|
||||
token,
|
||||
user,
|
||||
webserviceToken,
|
||||
};
|
||||
|
||||
await storage.setItem('NookAuthToken.' + nintendoAccountToken + '.' + user, existingToken);
|
||||
|
||||
return {
|
||||
nooklinkuser: new NooklinkUserApi(user, token.token, nooklink.gtoken, nooklink.useragent),
|
||||
data: existingToken,
|
||||
};
|
||||
}
|
||||
|
||||
debug('Using existing NookLink auth token');
|
||||
|
||||
return {
|
||||
nooklinkuser: new NooklinkUserApi(
|
||||
user, existingToken.token.token,
|
||||
existingToken.webserviceToken.gtoken, existingToken.webserviceToken.useragent
|
||||
),
|
||||
data: existingToken,
|
||||
};
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export const desc = 'Nintendo Switch Online';
|
||||
|
||||
export function builder(yargs: Argv<ParentArguments>) {
|
||||
for (const command of Object.values(commands)) {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
yargs.command(command);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
|
||||
const debug = createDebug('cli:nso:announcements');
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import createDebug from 'debug';
|
||||
import * as crypto from 'crypto';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getNintendoAccountSessionToken } from '../../api/na.js';
|
||||
import { ZNCA_CLIENT_ID } from '../../api/znc.js';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import Table from '../util/table.js';
|
||||
import { PresenceState } from '../../api/znc-types.js';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, hrduration, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
|
||||
const debug = createDebug('cli:nso:friends');
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import * as net from 'net';
|
||||
import createDebug from 'debug';
|
||||
import express, { Request, Response } from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
import * as net from 'net';
|
||||
import { v4 as uuidgen } from 'uuid';
|
||||
import { Announcement, CurrentUser, Friend, GetActiveEventResult, Presence, WebService } from '../../api/znc-types.js';
|
||||
import ZncApi from '../../api/znc.js';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken, SavedToken } from './util.js';
|
||||
import { getToken, SavedToken } from '../../common/auth/nso.js';
|
||||
import { NotificationManager, ZncNotifications } from '../../common/notify.js';
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -4,8 +4,8 @@ import persist from 'node-persist';
|
||||
import notifier from 'node-notifier';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getIksmToken } from '../splatnet2/util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
import { EmbeddedSplatNet2Monitor, NotificationManager, ZncNotifications } from '../../common/notify.js';
|
||||
import { CurrentUser, Friend, Game } from '../../api/znc-types.js';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import { PresencePermissions } from '../../api/znc-types.js';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
|
||||
const debug = createDebug('cli:nso:permissions');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { DiscordPresencePlayTime } from '../../discord/util.js';
|
||||
import { handleEnableSplatNet2Monitoring } from './notify.js';
|
||||
import { ZncDiscordPresence, ZncProxyDiscordPresence } from '../../common/presence.js';
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as util from 'util';
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
|
||||
const debug = createDebug('cli:nso:token');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
|
||||
const debug = createDebug('cli:nso:user');
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
import createDebug from 'debug';
|
||||
import * as persist from 'node-persist';
|
||||
import { FlapgApiResponse } from '../../api/f.js';
|
||||
import { NintendoAccountSessionTokenJwtPayload, NintendoAccountToken, NintendoAccountUser } from '../../api/na.js';
|
||||
import { Jwt } from '../../util.js';
|
||||
import { AccountLogin } from '../../api/znc-types.js';
|
||||
import ZncApi, { ZNCA_CLIENT_ID } from '../../api/znc.js';
|
||||
import ZncProxyApi from '../../api/znc-proxy.js';
|
||||
|
||||
const debug = createDebug('cli:nso');
|
||||
|
||||
export interface SavedToken {
|
||||
uuid: string;
|
||||
timestamp: string;
|
||||
nintendoAccountToken: NintendoAccountToken;
|
||||
user: NintendoAccountUser;
|
||||
flapg: FlapgApiResponse['result'];
|
||||
nsoAccount: AccountLogin;
|
||||
credential: AccountLogin['webApiServerCredential'];
|
||||
|
||||
expires_at: number;
|
||||
proxy_url?: string;
|
||||
}
|
||||
|
||||
export async function getToken(storage: persist.LocalStorage, token: string, proxy_url: string): Promise<{
|
||||
nso: ZncProxyApi;
|
||||
data: SavedToken;
|
||||
}>
|
||||
export async function getToken(storage: persist.LocalStorage, token: string, proxy_url?: string): Promise<{
|
||||
nso: ZncApi;
|
||||
data: SavedToken;
|
||||
}>
|
||||
export async function getToken(storage: persist.LocalStorage, token: string, proxy_url?: string) {
|
||||
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');
|
||||
}
|
||||
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(token);
|
||||
|
||||
if (jwt.payload.iss !== 'https://accounts.nintendo.com') {
|
||||
throw new Error('Invalid Nintendo Account session token issuer');
|
||||
}
|
||||
if (jwt.payload.typ !== 'session_token') {
|
||||
throw new Error('Invalid Nintendo Account session token type');
|
||||
}
|
||||
if (jwt.payload.aud !== ZNCA_CLIENT_ID) {
|
||||
throw new Error('Invalid Nintendo Account session token audience');
|
||||
}
|
||||
if (jwt.payload.exp <= (Date.now() / 1000)) {
|
||||
throw new Error('Nintendo Account session token expired');
|
||||
}
|
||||
|
||||
// Nintendo Account session tokens use a HMAC SHA256 signature, so we can't verify this is valid
|
||||
|
||||
const existingToken: SavedToken | undefined = await storage.getItem('NsoToken.' + token);
|
||||
|
||||
if (!existingToken || existingToken.expires_at <= Date.now()) {
|
||||
console.warn('Authenticating to Nintendo Switch Online app');
|
||||
debug('Authenticating to znc with session token');
|
||||
|
||||
const {nso, data} = proxy_url ?
|
||||
await ZncProxyApi.createWithSessionToken(proxy_url, token) :
|
||||
await ZncApi.createWithSessionToken(token);
|
||||
|
||||
const existingToken: SavedToken = {
|
||||
...data,
|
||||
expires_at: Date.now() + (data.credential.expiresIn * 1000),
|
||||
};
|
||||
|
||||
await storage.setItem('NsoToken.' + token, existingToken);
|
||||
await storage.setItem('NintendoAccountToken.' + data.user.id, token);
|
||||
|
||||
return {nso, data: existingToken};
|
||||
}
|
||||
|
||||
debug('Using existing token');
|
||||
await storage.setItem('NintendoAccountToken.' + existingToken.user.id, token);
|
||||
|
||||
return {
|
||||
nso: proxy_url ?
|
||||
new ZncProxyApi(proxy_url, token) :
|
||||
new ZncApi(existingToken.credential.accessToken),
|
||||
data: existingToken,
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
|
||||
const debug = createDebug('cli:nso:webservices');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
|
||||
const debug = createDebug('cli:nso:webservicetoken');
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import fetch from 'node-fetch';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import { Argv, initStorage } from '../../util.js';
|
||||
import { getToken } from './util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
import { AuthPolicy, AuthToken } from './http-server.js';
|
||||
|
||||
const debug = createDebug('cli:nso:znc-proxy-tokens');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../cli.js';
|
||||
import { Argv } from '../util.js';
|
||||
import { Argv, YargsArguments } from '../util.js';
|
||||
import * as commands from './pctl/index.js';
|
||||
|
||||
const debug = createDebug('cli:pctl');
|
||||
@@ -13,4 +13,8 @@ export function builder(yargs: Argv<ParentArguments>) {
|
||||
// @ts-expect-error
|
||||
yargs.command(command);
|
||||
}
|
||||
|
||||
return yargs;
|
||||
}
|
||||
|
||||
export type Arguments = YargsArguments<ReturnType<typeof builder>>;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as util from 'util';
|
||||
import * as crypto from 'crypto';
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../pctl.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getPctlToken } from './util.js';
|
||||
import { getPctlToken } from '../../common/auth/moon.js';
|
||||
import { getNintendoAccountSessionToken } from '../../api/na.js';
|
||||
import { ZNMA_CLIENT_ID } from '../../api/moon.js';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../pctl.js';
|
||||
import { ArgumentsCamelCase, Argv, hrduration, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getPctlToken } from './util.js';
|
||||
import { getPctlToken } from '../../common/auth/moon.js';
|
||||
|
||||
const debug = createDebug('cli:pctl:daily-summaries');
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../pctl.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getPctlToken } from './util.js';
|
||||
import { getPctlToken } from '../../common/auth/moon.js';
|
||||
|
||||
const debug = createDebug('cli:pctl:devices');
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import * as path from 'path';
|
||||
import * as fs from 'fs/promises';
|
||||
import createDebug from 'debug';
|
||||
import mkdirp from 'mkdirp';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../pctl.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getPctlToken } from './util.js';
|
||||
import { getPctlToken } from '../../common/auth/moon.js';
|
||||
import { DailySummaryResult } from '../../api/moon-types.js';
|
||||
import MoonApi from '../../api/moon.js';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../pctl.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getPctlToken } from './util.js';
|
||||
import { getPctlToken } from '../../common/auth/moon.js';
|
||||
|
||||
const debug = createDebug('cli:pctl:monthly-summaries');
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../pctl.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getPctlToken } from './util.js';
|
||||
import { getPctlToken } from '../../common/auth/moon.js';
|
||||
|
||||
const debug = createDebug('cli:pctl:monthly-summary');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../pctl.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getPctlToken } from './util.js';
|
||||
import { getPctlToken } from '../../common/auth/moon.js';
|
||||
|
||||
const debug = createDebug('cli:pctl:settings');
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as util from 'util';
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../pctl.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getPctlToken } from './util.js';
|
||||
import { getPctlToken } from '../../common/auth/moon.js';
|
||||
|
||||
const debug = createDebug('cli:pctl:token');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../pctl.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getPctlToken } from './util.js';
|
||||
import { getPctlToken } from '../../common/auth/moon.js';
|
||||
|
||||
const debug = createDebug('cli:pctl:user');
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import createDebug from 'debug';
|
||||
import * as persist from 'node-persist';
|
||||
import { ZNMA_CLIENT_ID } from '../../api/moon.js';
|
||||
import { NintendoAccountSessionTokenJwtPayload, NintendoAccountToken, NintendoAccountUser } from '../../api/na.js';
|
||||
import { Jwt } from '../../util.js';
|
||||
import MoonApi from '../../api/moon.js';
|
||||
|
||||
const debug = createDebug('cli:nso');
|
||||
|
||||
export interface SavedMoonToken {
|
||||
nintendoAccountToken: NintendoAccountToken;
|
||||
user: NintendoAccountUser;
|
||||
|
||||
expires_at: number;
|
||||
}
|
||||
|
||||
export async function getPctlToken(storage: persist.LocalStorage, token: string) {
|
||||
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');
|
||||
}
|
||||
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(token);
|
||||
|
||||
if (jwt.payload.iss !== 'https://accounts.nintendo.com') {
|
||||
throw new Error('Invalid Nintendo Account session token issuer');
|
||||
}
|
||||
if (jwt.payload.typ !== 'session_token') {
|
||||
throw new Error('Invalid Nintendo Account session token type');
|
||||
}
|
||||
if (jwt.payload.aud !== ZNMA_CLIENT_ID) {
|
||||
throw new Error('Invalid Nintendo Account session token audience');
|
||||
}
|
||||
if (jwt.payload.exp <= (Date.now() / 1000)) {
|
||||
throw new Error('Nintendo Account session token expired');
|
||||
}
|
||||
|
||||
// Nintendo Account session tokens use a HMAC SHA256 signature, so we can't verify this is valid
|
||||
|
||||
const existingToken: SavedMoonToken | undefined = await storage.getItem('MoonToken.' + token);
|
||||
|
||||
if (!existingToken || existingToken.expires_at <= Date.now()) {
|
||||
console.warn('Authenticating to Nintendo Switch Parental Controls app');
|
||||
debug('Authenticating to pctl with session token');
|
||||
|
||||
const {moon, data} = await MoonApi.createWithSessionToken(token);
|
||||
|
||||
const existingToken: SavedMoonToken = {
|
||||
...data,
|
||||
expires_at: Date.now() + (data.nintendoAccountToken.expires_in * 1000),
|
||||
};
|
||||
|
||||
await storage.setItem('MoonToken.' + token, existingToken);
|
||||
await storage.setItem('NintendoAccountToken-pctl.' + data.user.id, token);
|
||||
|
||||
return {moon, data: existingToken};
|
||||
}
|
||||
|
||||
debug('Using existing token');
|
||||
await storage.setItem('NintendoAccountToken-pctl.' + existingToken.user.id, token);
|
||||
|
||||
return {
|
||||
moon: new MoonApi(existingToken.nintendoAccountToken.access_token!, existingToken.user.id),
|
||||
data: existingToken,
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:battles');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:challenges');
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import createDebug from 'debug';
|
||||
import mkdirp from 'mkdirp';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
import { dumpChallenges, dumpHeroRecords, dumpProfileImage, dumpRecords } from '../../common/splatnet2/dump-records.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:dump-records');
|
||||
|
||||
@@ -3,7 +3,7 @@ import createDebug from 'debug';
|
||||
import mkdirp from 'mkdirp';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
import { dumpCoopResults, dumpResults } from '../../common/splatnet2/dump-results.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:dump-results');
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:hero');
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as path from 'path';
|
||||
import createDebug from 'debug';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { SplatNet2RecordsMonitor } from '../../common/splatnet2/monitor.js';
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:schedule');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:stages');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:user');
|
||||
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
import createDebug from 'debug';
|
||||
import persist from 'node-persist';
|
||||
import * as fs from 'fs';
|
||||
import { getToken } from '../nso/util.js';
|
||||
import SplatNet2Api from '../../api/splatnet2.js';
|
||||
import { WebServiceToken } from '../../api/znc-types.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2');
|
||||
|
||||
export interface SavedIksmSessionToken {
|
||||
webserviceToken: WebServiceToken;
|
||||
url: string;
|
||||
cookies: string;
|
||||
|
||||
iksm_session: string;
|
||||
expires_at: number;
|
||||
useragent: string;
|
||||
|
||||
last_used?: number;
|
||||
}
|
||||
|
||||
export async function getIksmToken(storage: persist.LocalStorage, token: string, proxy_url?: string, allow_fetch_token = false) {
|
||||
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');
|
||||
}
|
||||
|
||||
const existingToken: SavedIksmSessionToken | undefined = await storage.getItem('IksmToken.' + token);
|
||||
|
||||
const td = 24 * 60 * 60 * 1000; // 1 day in ms
|
||||
const last_used_days_ago = existingToken?.last_used && (existingToken.last_used + td) <= Date.now();
|
||||
const expired = existingToken && existingToken.expires_at <= Date.now();
|
||||
|
||||
if (!existingToken || ((!existingToken.last_used || last_used_days_ago) && expired)) {
|
||||
if (!allow_fetch_token) {
|
||||
throw new Error('No valid iksm_session cookie');
|
||||
}
|
||||
|
||||
console.warn('Authenticating to SplatNet 2');
|
||||
debug('Authenticating to SplatNet 2');
|
||||
|
||||
const {nso, data} = await getToken(storage, token, proxy_url);
|
||||
|
||||
const existingToken: SavedIksmSessionToken = await SplatNet2Api.loginWithZnc(nso, data.user);
|
||||
|
||||
await storage.setItem('IksmToken.' + token, existingToken);
|
||||
|
||||
if (!iksm_sessions.has(existingToken.iksm_session)) {
|
||||
iksm_sessions.set(existingToken.iksm_session, [storage, token, null, null]);
|
||||
}
|
||||
|
||||
return {
|
||||
splatnet: new SplatNet2Api(existingToken.iksm_session, existingToken.useragent),
|
||||
data: existingToken,
|
||||
};
|
||||
}
|
||||
|
||||
debug('Using existing token');
|
||||
|
||||
if (!iksm_sessions.has(existingToken.iksm_session)) {
|
||||
iksm_sessions.set(existingToken.iksm_session, [storage, token, null, null]);
|
||||
}
|
||||
|
||||
return {
|
||||
splatnet: new SplatNet2Api(existingToken.iksm_session, existingToken.useragent),
|
||||
data: existingToken,
|
||||
};
|
||||
}
|
||||
|
||||
export async function renewIksmToken(splatnet: SplatNet2Api, storage: persist.LocalStorage, token: string, proxy_url?: string) {
|
||||
console.warn('Authenticating to SplatNet 2');
|
||||
debug('Authenticating to SplatNet 2');
|
||||
|
||||
const {nso, data} = await getToken(storage, token, proxy_url);
|
||||
|
||||
const existingToken: SavedIksmSessionToken = await SplatNet2Api.loginWithZnc(nso, data.user);
|
||||
|
||||
await storage.setItem('IksmToken.' + token, existingToken);
|
||||
|
||||
if (!iksm_sessions.has(existingToken.iksm_session)) {
|
||||
iksm_sessions.set(existingToken.iksm_session, [storage, token, null, null]);
|
||||
}
|
||||
|
||||
iksm_sessions.delete(splatnet.iksm_session);
|
||||
|
||||
splatnet.iksm_session = existingToken.iksm_session;
|
||||
splatnet.useragent = existingToken.useragent;
|
||||
}
|
||||
|
||||
const iksm_sessions = new Map<string, [persist.LocalStorage, string, number | null, NodeJS.Timeout | null]>();
|
||||
|
||||
export function updateIksmSessionLastUsed(iksm_session: string, last_used: number = Date.now()) {
|
||||
const match = iksm_sessions.get(iksm_session);
|
||||
if (!match) return;
|
||||
|
||||
const [storage, token,, timeout] = match;
|
||||
|
||||
const new_timeout = timeout ?? setTimeout(() => {
|
||||
const match = iksm_sessions.get(iksm_session);
|
||||
if (!match) return;
|
||||
|
||||
const [storage, token, last_used, timeout] = match;
|
||||
if (timeout === new_timeout) match[3] = null;
|
||||
|
||||
writeUpdatedIksmSessionLastUsed(storage, token, last_used!);
|
||||
match[2] = null;
|
||||
}, 1000);
|
||||
|
||||
iksm_sessions.set(iksm_session, [storage, token, last_used, new_timeout]);
|
||||
}
|
||||
|
||||
function writeUpdatedIksmSessionLastUsed(storage: persist.LocalStorage, token: string, last_used: number) {
|
||||
const datum_str = fs.readFileSync(storage.getDatumPath('IksmToken.' + token), 'utf-8');
|
||||
const datum: persist.Datum = storage.parse(datum_str);
|
||||
const data: SavedIksmSessionToken = datum.value;
|
||||
|
||||
if (data.last_used && data.last_used >= last_used) return;
|
||||
|
||||
data.last_used = last_used;
|
||||
|
||||
const new_datum_str = storage.stringify(datum);
|
||||
fs.writeFileSync(storage.getDatumPath('IksmToken.' + token), new_datum_str, 'utf-8');
|
||||
}
|
||||
|
||||
function writeUpdatedIksmSessionsLastUsed() {
|
||||
for (const [iksm_session, data] of iksm_sessions) {
|
||||
const [storage, token, last_used, timeout] = data;
|
||||
if (timeout) clearTimeout(timeout), data[3] = null;
|
||||
if (!last_used) continue;
|
||||
|
||||
writeUpdatedIksmSessionLastUsed(storage, token, last_used);
|
||||
data[2] = null;
|
||||
}
|
||||
}
|
||||
|
||||
process.on('exit', () => writeUpdatedIksmSessionsLastUsed());
|
||||
process.on('uncaughtExceptionMonitor', () => writeUpdatedIksmSessionsLastUsed());
|
||||
@@ -2,7 +2,7 @@ import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import { getIksmToken } from '../../common/auth/splatnet2.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:weapons');
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import createDebug from 'debug';
|
||||
import Table from './util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../cli.js';
|
||||
import { Argv, initStorage } from '../util.js';
|
||||
import { SavedToken } from './nso/util.js';
|
||||
import { SavedMoonToken } from './pctl/util.js';
|
||||
import { SavedToken } from '../common/auth/nso.js';
|
||||
import { SavedMoonToken } from '../common/auth/moon.js';
|
||||
|
||||
const debug = createDebug('cli:users');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as crypto from 'crypto';
|
||||
import { Buffer } from 'buffer';
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../util.js';
|
||||
import { Argv } from '../../util.js';
|
||||
|
||||
const debug = createDebug('cli:util:captureid');
|
||||
|
||||
@@ -2,10 +2,10 @@ import createDebug from 'debug';
|
||||
import fetch from 'node-fetch';
|
||||
import { getPresenceFromUrl } from '../../api/znc-proxy.js';
|
||||
import { ActiveEvent, CurrentUser, Friend, Game, Presence, PresenceState } from '../../api/znc-types.js';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../util.js';
|
||||
import { DiscordPresenceContext, DiscordPresencePlayTime, getDiscordPresence, getInactiveDiscordPresence } from '../../discord/util.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getToken } from '../nso/util.js';
|
||||
import { getToken } from '../../common/auth/nso.js';
|
||||
|
||||
const debug = createDebug('cli:util:discord-activity');
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../util.js';
|
||||
import { DiscordRpcClient, getAllIpcSockets } from '../../discord/rpc.js';
|
||||
import { defaultTitle } from '../../discord/titles.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util.js';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import createDebug from 'debug';
|
||||
import fetch from 'node-fetch';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../util.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util.js';
|
||||
import { titles as unsorted_titles } from '../../discord/titles.js';
|
||||
import { DiscordApplicationRpc, getDiscordApplicationRpc } from './discord-activity.js';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../../cli.js';
|
||||
import type { Arguments as ParentArguments } from '../util.js';
|
||||
import { ArgumentsCamelCase } from '../../util.js';
|
||||
import * as publishers from '../../discord/titles/index.js';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user