0.0.4 • Published 2 years ago

@nxpkmn/login v0.0.4

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

@nxpkmn/login

Test Status License npm version

Logic for authenticating with Pokémon Showdown.

Installation

$ npm install @nxpkmn/login

Alternatively, as detailed below, if you are using @nxpkmn/login in the browser and want a convenient way to get started, simply depend on a transpiled and minified version via unpkg:

<script src="https://unpkg.com/@nxpkmn/login"></script>

Usage

In order to authenticate with Pokémon Showdown, a client must be able to make HTTP(S) requests to Pokémon Showdown's "login server" and to be able to communicate with its "sim server" via a WebSocket connection. A wide variety of libraries for simplifying these networking tasks exist, and as such, @nxpkmn/login was designed to be completely agnostic to the client's choice of network layer. @pkmn recommends fetch and ws, but @nxpkmn/login will work equally well with any native or third-party network library.

@nxpkmn/login provides helpers for several authentication "actions", though Pokémon Showdown expects a particular login flow and attempting actions in an unexpected order will result in confusing error messages:

Any user of Pokémon Showdown begins as a "guest" user. Pokémon Showdown's client will then attempt an upkeep action during initial load to extend any existing credentials for a registered user. This requires the sid cookie to be sent along with the request to identify which credentials are being prolonged. If there are no credentials to upkeep, the user remains as a guest until a rename is attempted. The rename may be unsuccessful because the name attempted requires authentication, at which point a login action is required. If the password is incorrect during login the server will respond with an obtuse error message about attempting to rename yourself as guest, this is just a 'fun' Pokémon Showdown gotcha. If a name is unregistered you may register the name with a password. Finally, you may logout of a name to terminate a session.

Examples

The following examples demonstrate how to use @nxpkmn/login with various HTTP APIs (which tend to be less homogenous than WebSocket APIs), but a WebSocket is also required to authenticate with Pokémon Showdown. For example purposes, assume a WebSocket named ws has been set up as below:

import * as WebSocket from 'websocket'
import {Protocol} from '@nxpkmn/protocol';

const server = 'sim.smogon.com';
const serverport = 8000;
const ws = new WebSocket(`ws://${server}:${serverport}/showdown/websocket`);

ws.on('open', ...);
ws.on('close', ...);
ws.on('error', ...);
ws.on('message', message => {
  for (const {args} of Protocol.parse(message)) {
    switch (args[0]) {
      case 'challstr': {
        const challstr = args[1];
        onChallstr(challstr);
        break;
      }
      case 'updateuser': {
        ...
      }
      ...
    }
  }
  });

To see a complete worked example, please see the login CLI which trivially logs in and out of Pokémon Showdown by leveraging @nxpkmn/login (though note that the majority of the complexity comes from setup). index.html provides a similar example for the browser.

http/https

Node.js's standard https library is somewhat verbose but perfectly functional for use with @nxpkmn/login:

import * as https from 'https';

import {Action, Actions} from '@nxpkmn/login';

function fetch(action: Action) {
  return new Promise((resolve, reject) => {
    let buf = '';
    const req = https.request(action.url, {
      method: action.method,
      headers: action.headers
    }, res => {
      if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`));
      res.on('data', d => {
        buf += d;
      });
      res.on('end', () => resolve(buf));
    });
    req.on('error', reject);
    req.write(action.data);
    req.end();
  });
}

async function onChallstr() {
  const action = Actions.login({username: 'User Name', password: 'password', challstr});
  const cmd = action.onResponse(await fetch(action));
  if (cmd) ws.send(cmd);
}

XMLHttpRequest

Similarly, the legacy XMLHttpRequest API can be used within browsers along with @nxpkmn/login to authenticate:

<script src="https://unpkg.com/@nxpkmn/login"></script>
<script>
  ...

  async function onChallstr() {
    const action = LoginTools.login({username: 'User Name', password: 'password', challstr});
    return new Promise((resolve, reject) => {
      const request = new XMLHttpRequest();
      request.addEventListener('load', () => {
        const cmd = action.onResponse(request.responseText);
        if (cmd) ws.send(cmd);
        resolve();
      });
      request.addEventListener('error', reject);
      for (const header in action.headers) {
        request.setRequestHeader(header, action.headers[header]);
      }
      request.open(action.method, action.url);
      request.send(action.data);
    });
  }
</script>

fetch

fetch is the new browser API for HTTP requests (node-fetch provides an isomorphic API for use åon with Node.js):

import fetch from 'node-fetch';

import {Actions} from '@nxpkmn/login';

async function onChallstr() {
  const action = Actions.login({username: 'User Name', password: 'password', challstr});
  const response = await (await fetch(action.url, {
    method: action.method,
    headers: action.headers,
    body: action.data,
  })).text();
  const cmd = action.onResponse(response);
  if (cmd) ws.send(cmd);
}

axios

axios is a very popular HTTP library which can be configured to work in the browser or on Node.js:

import axios from 'axios';

import {Actions} from '@nxpkmn/login';

async function onChallstr() {
  const action = Actions.login({username: 'User Name', password: 'password', challstr});
  const response = await axios({
    url: action.url,
    method: action.method,
    headers: action.headers,
    data: action.data,
    responseType: action.responseType,
  });
  const cmd = action.onResponse(response);
  if (cmd) ws.send(cmd);
}

jQuery

jQuery is still commonly used in the browser to provide cleaner APIs:

<script src="https://code.jquery.com/jquery.min.js"></script>
<script src="https://unpkg.com/@nxpkmn/login"></script>
<script>
  ...

  async function onChallstr() {
    const action = LoginTools.login({username: 'User Name', password: 'password', challstr});
    $.ajax(action.url, {
      method: action.method,
      headers: action.headers,
      data: action.data,
      success: data => {
        const cmd = action.onResponse(data);
        if (cmd) ws.send(cmd);
      },
      dataType: action.responseType,
    });
  }
</script>

Browser

The recommended way of using @nxpkmn/login in a web browser is to configure your bundler (Webpack, Rollup, Parcel, etc) to minimize it and package it with the rest of your application. If you do not use a bundler, a convenience production.min.js is included in the package. You simply need to depend on ./node_modules/@nxpkmn/login/build/production.min.js in a script tag (which is what the unpkg shortcut above is doing), after which LoginTools will be accessible as a global.

License

This package is distributed under the terms of the MIT License.