@pkmn/login
v0.2.0
Published
Logic for authenticating with Pokémon Showdown
Downloads
85
Readme
@pkmn/login
Logic for authenticating with Pokémon Showdown.
Installation
$ npm install @pkmn/login
Alternatively, as detailed below, if you are using @pkmn/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/@pkmn/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, @pkmn/login
was designed to be completely agnostic to the client's choice of network
layer. @pkmn
recommends fetch
and
ws
, but @pkmn/login
will work equally well with any native
or third-party network library.
@pkmn/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 @pkmn/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 '@pkmn/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 @pkmn/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 @pkmn/login
:
import * as https from 'https';
import {Action, Actions} from '@pkmn/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 @pkmn/login
to authenticate:
<script src="https://unpkg.com/@pkmn/login"></script>
<script>
...
async function onChallstr() {
const action = pkmn.login.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.js includes fetch
as of v18):
import fetch from 'node-fetch';
import {Actions} from '@pkmn/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 '@pkmn/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/@pkmn/login"></script>
<script>
...
async function onChallstr() {
const action = pkmn.login.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 @pkmn/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 index.min.js
is included in the
package. You simply need to depend on ./node_modules/@pkmn/login/build/index.min.js
in a
script
tag (which is what the unpkg shortcut above is doing), after which pkmn.login
will be
accessible as a global.
License
This package is distributed under the terms of the MIT License.