Skip to content

Instantly share code, notes, and snippets.

@bmingles
Last active October 30, 2024 14:51
Show Gist options
  • Save bmingles/536b21bab983b28037630a37ae709cd9 to your computer and use it in GitHub Desktop.
Save bmingles/536b21bab983b28037630a37ae709cd9 to your computer and use it in GitHub Desktop.
KeyGen
#!/usr/bin/env node
// Experiment with generating a key pair, uploading the public key to the server,
// and authenticating with the private key.
import {
generateBase64KeyPair,
loginClientWithKeyPair,
uploadPublicKey,
} from '@deephaven-enterprise/auth-nodejs'
import { loginPrompt } from './loginPrompt.mjs'
import { createDheClient, getDhe } from './utils.mjs'
const { serverUrl, username, password } = await loginPrompt()
const credentials = {
type: 'password',
username,
token: password,
}
const dhe = await getDhe(serverUrl)
const dheClient = await createDheClient(dhe, serverUrl)
const { publicKey, privateKey } = await generateBase64KeyPair()
console.log({ publicKey, privateKey })
await uploadPublicKey(dheClient, credentials, publicKey, 'ec')
const keyPairCredentials = {
type: 'keyPair',
username: credentials.username,
keyPair: {
type: 'ec',
publicKey,
privateKey,
},
}
await loginClientWithKeyPair(
await createDheClient(dhe, serverUrl),
keyPairCredentials,
)
console.log('Yeah!!')
process.exit(0)
/**
* Prompt the user for information to connect to a DH server.
*/
import fs from 'node:fs'
import path from 'node:path'
import { read } from 'read'
if (typeof globalThis.__dirname === 'undefined') {
globalThis.__dirname = import.meta.dirname
}
const AUTO_COMPLETE_PATH = path.join(
__dirname,
'..',
'..',
'tabAutocomplete.txt',
)
export async function loginPrompt() {
const completions = getAutoComplete(AUTO_COMPLETE_PATH)
const serverUrlRaw = await read({
prompt: 'Enter the Deephaven server URL: ',
completer: (input) => {
const filtered = completions.filter((c) => c.includes(input))
return [filtered, input]
},
})
const serverUrl = new URL(serverUrlRaw)
const username = await read({ prompt: 'Enter your username: ' })
const password = await read({
prompt: 'Enter your password: ',
replace: '*',
silent: true,
})
return {
serverUrl,
username,
password,
}
}
// Optionally provide a list of server URLs to `tab` autocompletions via serverList.txt.
function getAutoComplete(autoCompletePath) {
if (!fs.existsSync(autoCompletePath)) {
return []
}
return String(fs.readFileSync(autoCompletePath)).split('\n')
}
{
"name": "generate-key-pair",
"description": "Generate Key Pair",
"version": "0.0.1",
"bin": "./index.mjs",
"dependencies": {
"@deephaven-enterprise/auth-nodejs": "1.20240723.107-alpha-auth-nodejs.23555",
"@deephaven/jsapi-nodejs": "^0.96.2-alpha-jsapi-nodejs.7",
"read": "^4.0.0"
}
}
import { loadModules } from '@deephaven/jsapi-nodejs'
import path from 'node:path'
export async function getDhe(serverUrl) {
polyfill()
const tmpDir = path.join(__dirname, 'tmp')
// Download jsapi `ESM` files from DH Community server.
await loadModules({
serverUrl,
serverPaths: ['irisapi/irisapi.nocache.js'],
download: true,
storageDir: tmpDir,
sourceModuleType: 'cjs',
targetModuleType: 'esm',
})
// DHE currently exposes the jsapi via the global `iris` object.
return iris
}
export function polyfill() {
// These will eventually not be needed once JSAPI is updated to not rely on `window` and `self`.
// @ts-ignore
globalThis.self = globalThis
// @ts-ignore
globalThis.window = globalThis
// This is needed to mimic running in a local http browser environment when
// making requests to the server. This at least impacts websocket connections.
// Not sure if it is needed for other requests. The url is an arbitrary
// non-https url just to make it stand out in logs.
// @ts-ignore
global.window.location = new URL('http://deephaven-keygen.localhost/')
}
export async function createDheClient(dhe, serverUrl) {
const dheClient = new dhe.Client(getWsUrl(serverUrl).toString())
return new Promise((resolve) => {
const unsubscribe = dheClient.addEventListener(
dhe.Client.EVENT_CONNECT,
() => {
unsubscribe()
resolve(dheClient)
},
)
})
}
/**
* Get the WebSocket URL for a DHE server URL.
* @param serverUrl The DHE server URL.
* @returns The WebSocket URL.
*/
export function getWsUrl(serverUrl) {
const url = new URL('/socket', serverUrl)
if (url.protocol === 'http:') {
url.protocol = 'ws:'
} else {
url.protocol = 'wss:'
}
return url
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment