impress/bin/neopass-server
Emi Matchu 3eeb5d1065 Actually create user from NeoPass authentication! <3 <3
Whew, exciting! Still done nothing against the live NeoPass server, but
we've got this fully working with the development server, it seems!
Wowie!!

This is all still hidden behind secret flags, so it's fine to deploy
live. (And it's not actually a problem if someone gets past to the
endpoints behind it, because we haven't actually set up real
credentials for our NeoPass client yet, so authentication will fail!)

Okay time to lie down lol.
2024-03-14 19:11:06 -07:00

111 lines
4.5 KiB
JavaScript
Executable file

#!/usr/bin/env node
/**
* A test NeoPass server! This is a very lean, hacky implementation, designed
* to just see the basic OAuth interactions Work At All.
*
* This server is an `oauth2-mock-server` instance that's easy to spin up and
* have perform OAuth for us. We give it a hardcoded development-only key, and
* it just auto-grants permissions!
*
* It slightly differs from the NeoPass spec, in that it uses different paths
* for its endpoints, but that's okay: DTI will use OpenID's "discovery"
* feature to discover those endpoints via a single well-known path, without
* needing them hardcoded.
*/
const fs = require("node:fs/promises");
const pathLib = require("node:path");
const { spawn } = require("node:child_process");
const urlLib = require("node:url");
const { OAuth2Server } = require("oauth2-mock-server");
const express = require("express");
// This is the Neopets username and email we'll report back to DTI when you
// authenticate through here.
const USERNAME = "test";
const EMAIL = "theneopetsteam@neopets.com";
const certPath = pathLib.join(__dirname, "..", "tmp", "localhost.pem");
const keyPath = pathLib.join(__dirname, "..", "tmp", "localhost-key.pem");
async function fileExists(path) {
try {
await fs.stat(path);
} catch (error) {
if (error.code === "ENOENT") {
return false;
}
throw error;
}
return true;
}
async function ensureCertsExist() {
if (!(await fileExists(certPath)) || !(await fileExists(keyPath))) {
console.log(
"Using mkcert to create localhost.pem and localhost-key.pem in " +
"the Rails tmp dir, to serve over HTTPS.",
);
const mkcertProc = spawn(
"mkcert",
["-cert-file", certPath, "-key-file", keyPath, "localhost"],
{ stdio: ["ignore", process.stdout, process.stderr] },
);
// Wait for the process to finish, raising an error if it fails.
await new Promise((resolve, reject) => {
mkcertProc.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`mkcert returned status ${code}`));
}
});
mkcertProc.on("error", (error) => {
reject(error);
});
});
}
}
async function startServer(port) {
const server = new OAuth2Server(keyPath, certPath);
await server.issuer.keys.add({
// A key we generated for the NeoPass test server. It's okay for its
// "secret" info to be here, because it's for development only!
kid: "neopass-server-DEVLOPMENT-ONLY-NOT-FOR-PRODUCTION",
p: "50btwsJlPbGLUFnCSBZzddyMX_oRQ8nz4lMrpAd4umPLqMUmS0NbBZNf6DI7s8PkRUxtV8KdvZh3OYWavFnbk55GDG4Y_J_wA4XlHU3d5KIfSNaIdbtVp4CFOq1lovho4sYX_26vcGgYb2Azeg7nz_gDpqNmIdJdKuZxzsrboK8",
kty: "RSA",
q: "xha0i9_lbOMQhmmni02Dtpocil26GI7W8xbOFyOvceBCRNf-XOA_-W_Xk9ItJRHnAWM1TML36PN0l864d4QAXbBo64FHu2cjdFKnXJNliJaPcOPAMQB_D8GSylU1gTwSpP_vVe8t232LeF1oBwbOoBIS-6NsOpLmL8Sezv6Fpac",
d: "WSUNeEd_EyaELK7wqT6GJJK_RfYjaE5h6USe9rD9cd_tQE2PaZWXMyZ4OCk5Z5hdG2ryZY7NYsOI2CPs8HCFBqMoKd0z0A0EgB8Dq2fe_-t5Rm0Zq1ZnI5tnBcZeQmw0hDT98Wg00FA53SSUqfnOgI_VuLvquM6f18_XQOKRRfTcwh1a4teDAH0g0s8FVOS5DANtg71mTdq5fEkWmQMD3qKC6SNrx3WXXHezDs0MWdeFqn9Dg7gssSqB7PnqB-hlC_fHnu4gm9nDqPTMzsJC2i8d3adm0AeORRCulGLe7hU-_TgTbZzgIYCgOK_asaewW-6Qk9qFj-J4djBaKIee-Q",
e: "AQAB",
use: "sig",
qi: "WNiwCcAk2x7e0KvuupL2DNU-JUjLEF9Onee5T9u9ihbgGSDIyP04_96TzCIK3wsY6lct64oOo0Er-z5cf_5eOBPD3n0eEL-JuKIgn0mEKrazJOnGQzlyeZPzk4dUO2J7D42ObopfYsoBIcJx-Y_43a6WORDMGSVCiURmKavTHUU",
dp: "p1_wj-Npq3VDElpzPQJqeuCrAoaSWhHcm21_hs0VdSbl6_UJ2qwbQnS-kudPx7A8El7WPw4MZHrjxdBIBImvXCzOGw7OrHz_ET2ka0nADUe7BlakGTgDLB7ZzHZSuNe36G5eTbCH7PyYunnPp0UERMEDu2RDdLSuUm7F7FdpDOc",
alg: "RS256",
dq: "purLCHKKKM7NRfYRsFiI_H2wPwfroHX8uqokz2rKk_Kc5NX9CNYOEmokBfO9BtenCIxIhX5k2G8NeD5BQrSAenIEdy5g-5FVVtevH1s023vDMyU29hOs_eHnh4d1poiwTUk8q_T3d1S7CZnr5r_drRSN2m1C7biLLwVHrLTceVE",
n: "svVfGU4NGcfBCmQiIOW5uzg5SAN2CWSIQSstnhqZoCdjy5OoKpKVR8O9TbDvxixrvkFyAav90Q0Xse8iFTcjfCKuqINYiuYMXhCvfBlc_DVVOQca9pMpN03LaDofd5Ll4_BFTtt1nSPahwWU7xDM-Bkkh_TcS2qS4N2xbpEGi0q0ZkrJN4WyiDBC2k9WbK-YHr4Rj4JKypFVSeBIrjxVPmlPzgfqlLGGIB0l92SnJDXDMlkWcCCTyLgqSBM04nkxGDSykq_ei76qCdRd7b10wMBaoS9DeBThAyHpur2LoPdH3gxbcwoWExi-jPlNP1LdKVZD8b95OY3CRyMAAMGdKQ",
});
server.service.on("beforeTokenSigning", (token) => {
token.payload.sub = USERNAME;
});
server.service.on("beforeUserinfo", (userInfoResponse) => {
userInfoResponse.body.email = EMAIL;
});
await server.start(port, "localhost");
console.log(`Started NeoPass development server at: ${server.issuer.url}`);
}
async function main() {
await ensureCertsExist();
await startServer(8585);
}
main().catch((error) => {
console.error(error);
});