iksm login

main
spacemeowx2 2022-10-18 20:08:26 +08:00
parent adf27c3b03
commit 8fcb758c97
6 changed files with 161 additions and 0 deletions

View File

@ -1,2 +1,10 @@
# s3si.ts # s3si.ts
Export your battles from SplatNet to stat.ink Export your battles from SplatNet to stat.ink
## Usage
1. Install deno
2. Run
`deno run --allow-net --allow-read --allow-write --allow-env https://raw.githubusercontent.com/spacemeowx2/s3si.ts/master/s3si.ts`

6
deps.ts Normal file
View File

@ -0,0 +1,6 @@
export {
Cookie,
CookieJar,
wrapFetch,
} from "https://deno.land/x/another_cookiejar@v4.1.4/mod.ts";
export * as base64 from "https://deno.land/std@0.95.0/encoding/base64.ts";

124
iksm.ts Normal file
View File

@ -0,0 +1,124 @@
import { CookieJar, wrapFetch } from "./deps.ts";
import { LoginState } from "./state.ts";
import { urlBase64Encode } from "./utils.ts";
const NSOAPP_VERSION = "2.3.1";
function random(size: number): ArrayBuffer {
return crypto.getRandomValues(new Uint8Array(size)).buffer;
}
async function getSessionToken({
cookieJar,
sessionTokenCode,
authCodeVerifier,
}: {
cookieJar: CookieJar;
sessionTokenCode: string;
authCodeVerifier: string;
}): Promise<string | undefined> {
const fetch = wrapFetch({ cookieJar });
const headers = {
"User-Agent": `OnlineLounge/${NSOAPP_VERSION} NASDKAPI Android`,
"Accept-Language": "en-US",
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": "540",
"Host": "accounts.nintendo.com",
"Connection": "Keep-Alive",
"Accept-Encoding": "gzip",
};
const body = {
"client_id": "71b963c1b7b6d119",
"session_token_code": sessionTokenCode,
"session_token_code_verifier": authCodeVerifier,
};
const url = "https://accounts.nintendo.com/connect/1.0.0/api/session_token";
const res = await fetch(url, {
method: "POST",
headers: headers,
body: new URLSearchParams(body),
});
const resBody = await res.json();
return resBody["session_token"];
}
export async function loginManually(): Promise<LoginState> {
const cookieJar = new CookieJar();
const fetch = wrapFetch({ cookieJar });
const state = urlBase64Encode(random(36));
const authCodeVerifier = urlBase64Encode(random(32));
const authCvHash = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(authCodeVerifier),
);
const authCodeChallenge = urlBase64Encode(authCvHash);
const headers = {
"Host": "accounts.nintendo.com",
"Connection": "keep-alive",
"Cache-Control": "max-age=0",
"Upgrade-Insecure-Requests": "1",
"User-Agent":
"Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Mobile Safari/537.36",
"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8n",
"DNT": "1",
"Accept-Encoding": "gzip,deflate,br",
};
const body = {
"state": 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": authCodeChallenge,
"session_token_code_challenge_method": "S256",
"theme": "login_form",
};
const url = "https://accounts.nintendo.com/connect/1.0.0/authorize?" +
new URLSearchParams(body);
const res = await fetch(
url,
{
method: "GET",
headers: headers,
},
);
console.log("Navigate to this URL in your browser:");
console.log(res.url);
const login = prompt(
'Log in, right click the "Select this account" button, copy the link address, and paste it below:\n',
);
if (!login) {
throw new Error("No login URL provided");
}
const loginURL = new URL(login);
const params = new URLSearchParams(loginURL.hash.substring(1));
const sessionTokenCode = params.get("session_token_code");
if (!sessionTokenCode) {
throw new Error("No session token code provided");
}
const sessionToken = await getSessionToken({
cookieJar,
sessionTokenCode,
authCodeVerifier,
});
if (!sessionToken) {
throw new Error("No session token found");
}
return {
sessionToken,
};
}

4
s3si.ts Normal file
View File

@ -0,0 +1,4 @@
import { loginManually } from "./iksm.ts";
const state = await loginManually();
console.log(state);

3
state.ts Normal file
View File

@ -0,0 +1,3 @@
export type LoginState = {
sessionToken: string;
};

16
utils.ts Normal file
View File

@ -0,0 +1,16 @@
import { base64 } from "./deps.ts";
export function urlBase64Encode(data: ArrayBuffer) {
return base64.encode(data)
.replaceAll("+", "_")
.replaceAll("/", "-")
.replaceAll("=", "");
}
export function urlBase64Decode(data: string) {
return base64.decode(
data
.replaceAll("_", "+")
.replaceAll("-", "/"),
);
}