mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-07-10 06:25:21 -05:00
Add a command to get a session token
This commit is contained in:
parent
5610cf8630
commit
5aba992899
|
|
@ -4,6 +4,35 @@ import { ErrorResponse } from './util.js';
|
|||
|
||||
const debug = createDebug('api:na');
|
||||
|
||||
export async function getNintendoAccountSessionToken(code: string, verifier: string, client_id: string) {
|
||||
debug('Getting Nintendo Account session token');
|
||||
|
||||
const response = await fetch('https://accounts.nintendo.com/connect/1.0.0/api/session_token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-Platform': 'Android',
|
||||
'X-ProductVersion': '2.0.0',
|
||||
'User-Agent': 'NASDKAPI; Android',
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
client_id,
|
||||
session_token_code: code,
|
||||
session_token_code_verifier: verifier,
|
||||
}).toString(),
|
||||
});
|
||||
|
||||
const token = await response.json() as NintendoAccountSessionToken | NintendoAccountError;
|
||||
|
||||
if ('errorCode' in token) {
|
||||
throw new ErrorResponse('[na] + ' + token.detail, response, token);
|
||||
}
|
||||
|
||||
debug('Got Nintendo Account session token', token);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
export async function getNintendoAccountToken(token: string) {
|
||||
debug('Getting Nintendo Account token');
|
||||
|
||||
|
|
@ -56,6 +85,11 @@ export async function getNintendoAccountUser(token: NintendoAccountToken) {
|
|||
return user;
|
||||
}
|
||||
|
||||
export interface NintendoAccountSessionToken {
|
||||
session_token: string;
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface NintendoAccountToken {
|
||||
scope: ['openid', 'user', 'user.birthday', 'user.mii', 'user.screenName'];
|
||||
token_type: 'Bearer';
|
||||
|
|
|
|||
99
src/cli/auth.ts
Normal file
99
src/cli/auth.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import * as util from 'util';
|
||||
import createDebug from 'debug';
|
||||
import * as crypto from 'crypto';
|
||||
import type { Arguments as ParentArguments } from '../cli.js';
|
||||
import { ArgumentsCamelCase, Argv, getToken, initStorage, YargsArguments } from '../util.js';
|
||||
import { getNintendoAccountSessionToken } from '../api/na.js';
|
||||
|
||||
const debug = createDebug('cli: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: 'npf71b963c1b7b6d119://auth',
|
||||
client_id: '71b963c1b7b6d119',
|
||||
scope: 'openid user user.birthday user.mii user.screenName',
|
||||
response_type: 'session_token_code',
|
||||
session_token_code_challenge: challenge,
|
||||
session_token_code_challenge_method: 'S256',
|
||||
theme: 'login_form',
|
||||
};
|
||||
|
||||
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 "npf71b963c1b7b6d119://auth".');
|
||||
console.log('');
|
||||
|
||||
const read = await import('read');
|
||||
// @ts-expect-error
|
||||
const prompt = util.promisify(read.default as typeof read);
|
||||
|
||||
const applink = await prompt({
|
||||
prompt: `Paste the link: `,
|
||||
output: process.stderr,
|
||||
});
|
||||
|
||||
console.log('');
|
||||
|
||||
const authorisedurl = new URL(applink);
|
||||
const authorisedparams = new URLSearchParams(authorisedurl.hash.substr(1));
|
||||
debug('Redirect URL parameters', [...authorisedparams.entries()]);
|
||||
|
||||
const token = await getNintendoAccountSessionToken(
|
||||
authorisedparams.get('session_token_code')!, verifier, '71b963c1b7b6d119');
|
||||
|
||||
console.log('Session token', token);
|
||||
|
||||
if (argv.auth) {
|
||||
const storage = await initStorage(argv.dataPath);
|
||||
|
||||
const {nso, data} = await getToken(storage, token.session_token, argv.zncProxyUrl);
|
||||
|
||||
console.log('Authenticated as Nintendo Account %s (NA %s, NSO %s)',
|
||||
data.user.screenName, data.user.nickname, data.nsoAccount.user.name);
|
||||
|
||||
await storage.setItem('NintendoAccountToken.' + 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export * as token from './token.js';
|
||||
export * as auth from './auth.js';
|
||||
export * as users from './users.js';
|
||||
export * as user from './user.js';
|
||||
export * as announcements from './announcements.js';
|
||||
|
|
|
|||
|
|
@ -12,10 +12,6 @@ export function builder(yargs: Argv<ParentArguments>) {
|
|||
return yargs.positional('token', {
|
||||
describe: 'Nintendo Account session token (it is recommended this is not set and you enter it interactively)',
|
||||
type: 'string',
|
||||
}).option('auth', {
|
||||
describe: 'Authenticate immediately',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
}).option('select', {
|
||||
describe: 'Set as default user (default: true if only user)',
|
||||
type: 'boolean',
|
||||
|
|
@ -39,26 +35,22 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
|||
});
|
||||
}
|
||||
|
||||
await storage.setItem('SessionToken', argv.token);
|
||||
const {nso, data} = await getToken(storage, argv.token, argv.zncProxyUrl);
|
||||
|
||||
if (argv.auth) {
|
||||
const {nso, data} = await getToken(storage, argv.token, argv.zncProxyUrl);
|
||||
console.warn('Authenticated as Nintendo Account %s (NA %s, NSO %s)',
|
||||
data.user.screenName, data.user.nickname, data.nsoAccount.user.name);
|
||||
|
||||
console.log('Authenticated as Nintendo Account %s (NA %s, NSO %s)',
|
||||
data.user.screenName, data.user.nickname, data.nsoAccount.user.name);
|
||||
await storage.setItem('NintendoAccountToken.' + data.user.id, argv.token);
|
||||
|
||||
await storage.setItem('NintendoAccountToken.' + data.user.id, argv.token);
|
||||
const users = new Set(await storage.getItem('NintendoAccountIds') ?? []);
|
||||
users.add(data.user.id);
|
||||
await storage.setItem('NintendoAccountIds', [...users]);
|
||||
|
||||
const users = new Set(await storage.getItem('NintendoAccountIds') ?? []);
|
||||
users.add(data.user.id);
|
||||
await storage.setItem('NintendoAccountIds', [...users]);
|
||||
console.log('Saved token');
|
||||
|
||||
if ('select' in argv ? argv.select : users.size === 1) {
|
||||
await storage.setItem('SelectedUser', data.user.id);
|
||||
if ('select' in argv ? argv.select : users.size === 1) {
|
||||
await storage.setItem('SelectedUser', data.user.id);
|
||||
|
||||
console.log('Set as default user');
|
||||
}
|
||||
} else {
|
||||
console.log('Saved token');
|
||||
console.log('Set as default user');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user