mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-03-22 10:24:17 -05:00
111 lines
3.8 KiB
TypeScript
111 lines
3.8 KiB
TypeScript
import * as crypto from 'node:crypto';
|
|
import createDebug from 'debug';
|
|
import type { Arguments as ParentArguments } from '../pctl.js';
|
|
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
|
import { initStorage } from '../../util/storage.js';
|
|
import { getPctlToken } from '../../common/auth/moon.js';
|
|
import { getNintendoAccountSessionToken } from '../../api/na.js';
|
|
import { ZNMA_CLIENT_ID } from '../../api/moon.js';
|
|
import prompt from '../util/prompt.js';
|
|
|
|
const debug = createDebug('cli:pctl:auth');
|
|
|
|
export const command = 'auth';
|
|
export const desc = 'Generate a link to login to a Nintendo Account';
|
|
|
|
export function builder(yargs: Argv<ParentArguments>) {
|
|
return yargs.option('auth', {
|
|
describe: 'Authenticate immediately',
|
|
type: 'boolean',
|
|
default: true,
|
|
}).option('select', {
|
|
describe: 'Set as default user (default: true if only user)',
|
|
type: 'boolean',
|
|
});
|
|
}
|
|
|
|
type Arguments = YargsArguments<ReturnType<typeof builder>>;
|
|
|
|
export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
|
const state = crypto.randomBytes(36).toString('base64url');
|
|
const verifier = crypto.randomBytes(32).toString('base64url');
|
|
const challenge = crypto.createHash('sha256').update(verifier).digest().toString('base64url');
|
|
|
|
const params = {
|
|
state,
|
|
redirect_uri: 'npf54789befb391a838://auth',
|
|
client_id: ZNMA_CLIENT_ID,
|
|
scope: [
|
|
'openid',
|
|
'user',
|
|
'user.mii',
|
|
'moonUser:administration',
|
|
'moonDevice:create',
|
|
'moonOwnedDevice:administration',
|
|
'moonParentalControlSetting',
|
|
'moonParentalControlSetting:update',
|
|
'moonParentalControlSettingState',
|
|
'moonPairingState',
|
|
'moonSmartDevice:administration',
|
|
'moonDailySummary',
|
|
'moonMonthlySummary',
|
|
].join(' '),
|
|
response_type: 'session_token_code',
|
|
session_token_code_challenge: challenge,
|
|
session_token_code_challenge_method: 'S256',
|
|
};
|
|
|
|
const authoriseurl = 'https://accounts.nintendo.com/connect/1.0.0/authorize?' +
|
|
new URLSearchParams(params).toString();
|
|
|
|
debug('Authentication parameters', {
|
|
state,
|
|
verifier,
|
|
challenge,
|
|
}, params);
|
|
|
|
console.log('1. Open this URL and login to your Nintendo Account:');
|
|
console.log('');
|
|
console.log(authoriseurl);
|
|
console.log('');
|
|
|
|
console.log('2. On the "Linking an External Account" page, right click "Select this person" and copy the link. It should start with "npf54789befb391a838://auth".');
|
|
console.log('');
|
|
|
|
const applink = await prompt({
|
|
prompt: `Paste the link: `,
|
|
});
|
|
|
|
console.log('');
|
|
|
|
const authorisedurl = new URL(applink);
|
|
const authorisedparams = new URLSearchParams(authorisedurl.hash.substr(1));
|
|
debug('Redirect URL parameters', [...authorisedparams.entries()]);
|
|
|
|
const code = authorisedparams.get('session_token_code')!;
|
|
const token = await getNintendoAccountSessionToken(code, verifier, ZNMA_CLIENT_ID);
|
|
|
|
console.log('Session token', token);
|
|
|
|
if (argv.auth) {
|
|
const storage = await initStorage(argv.dataPath);
|
|
|
|
const {moon, data} = await getPctlToken(storage, token.session_token);
|
|
|
|
console.log('Authenticated as Nintendo Account %s (%s)',
|
|
data.user.nickname, data.user.id);
|
|
|
|
await storage.setItem('NintendoAccountToken-pctl.' + data.user.id, token.session_token);
|
|
|
|
const users = new Set(await storage.getItem('NintendoAccountIds') ?? []);
|
|
users.add(data.user.id);
|
|
await storage.setItem('NintendoAccountIds', [...users]);
|
|
|
|
if ('select' in argv ? argv.select : users.size === 1) {
|
|
await storage.setItem('SelectedUser', data.user.id);
|
|
|
|
console.log('Set as default user');
|
|
}
|
|
}
|
|
}
|