UNPKG

xlicore

Version:

Currently only used for my own launcher, proper docs will come later (hopefully).

121 lines (120 loc) 4.06 kB
import ky from 'ky'; import * as DraslAuthenticate from '../types/meta/auth/drasl/authenticate.js'; import path from 'path'; import fs from 'fs'; import fsp from 'fs/promises'; import fswin from 'fswin'; export class DraslAuth { opts; authserver; authFilepath; constructor(opts) { this.opts = opts; this.authserver = opts.server; if (opts.saveDir) this.authFilepath = path.resolve(opts.saveDir, '.super_secret.json'); } async init(creds) { let json; if (this.authFilepath && fs.existsSync(this.authFilepath)) { json = await fsp.readFile(this.authFilepath, { encoding: 'utf8' }).then(async (val) => { const json = JSON.parse(val); if (!DraslAuthenticate.isValidResponse(json) || !(await this.validate(json))) { return await this.refresh(json); } return json; }); } else { json = await this.first(creds); } return { accessToken: json.accessToken, clientId: json.clientToken, userType: 'mojang', uuid: json.selectedProfile.id, name: json.selectedProfile.name, drasl: { server: this.opts.server } }; } async first(creds) { if (!creds.password) throw new Error('[Drasl Auth] Password not specified!'); const body = { agent: { name: 'Minecraft', version: 1 }, username: creds.username, password: creds.password, requestUser: true }; const resp = await ky .post(this.authserver + '/authenticate', { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }) .json(); await this.writeJson(resp); return resp; } async refresh(body) { const resp = await ky .post(this.authserver + '/refresh', { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }) .json(); await this.writeJson(resp); return resp; } async invalidate(body) { await ky.post(this.authserver + '/refresh', { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); } async writeJson(json) { if (!this.authFilepath) return; fswin.setAttributesSync(this.authFilepath, { IS_HIDDEN: false }); return await fsp.writeFile(this.authFilepath, JSON.stringify(json), { encoding: 'utf8' }).then(() => { fswin.setAttributesSync(this.authFilepath, { IS_HIDDEN: true }); // FIXME: if porting to other OSes }); } async validate(json) { const body = { accessToken: json.accessToken, clientToken: json.clientToken }; const resp = await ky.post(this.authserver + '/validate', { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (resp.status == 204) return true; if (resp.status == 403) return false; throw new Error(`Unhandled status ${resp.status}: ${resp.statusText}!`); } async signout(creds) { if (!creds.password) throw new Error(`No password! Can't sign out.`); const body = { username: creds.username, password: creds.password }; const resp = await ky.post(this.authserver + 'signout', { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (resp.status == 401) throw new Error(`Invalid credentials! Can't sign out.`); if (resp.status != 204) throw new Error(`Unhandled status ${resp.status}: ${resp.statusText}!`); } }