Skip to content

Commit

Permalink
Parameterize jwk path
Browse files Browse the repository at this point in the history
  • Loading branch information
gergas3 committed Aug 31, 2023
1 parent 4b4d152 commit 05a9c34
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 18 deletions.
1 change: 1 addition & 0 deletions .github/workflows/docker.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
on:
pull_request: {}
workflow_call:
outputs:
docker_image_tags:
Expand Down
9 changes: 8 additions & 1 deletion cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ void yargs(hideBin(process.argv))
demandOption: true,
describe: "The clientId used to identify this client",
})
.option("jwkPath", {
type: "string",
demandOption: true,
describe:
"The path to the directory on the file system where JWK is stored",
})
.option("tunnelHost", {
type: "string",
demandOption: true,
Expand All @@ -67,13 +73,14 @@ void yargs(hideBin(process.argv))
const {
targetUrl,
clientId,
jwkPath,
tunnelHost: host,
tunnelPort: port,
insecure,
} = args;
const backoff = new Backoff(1, 3000); // Aggressively retry starting at 1ms delay
const client = new JsonRpcClient(
{ targetUrl, clientId },
{ targetUrl, clientId, jwkPath },
{ host, port, insecure, backoff }
);
logger.info({ args }, "Running JSON-RPC client");
Expand Down
8 changes: 5 additions & 3 deletions client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class JsonRpcClient {
#targetUrl: string;
#targetHostName: string;
#clientId: string;
#jwkPath: string;
#logger: Logger;

#backoff?: Backoff;
Expand All @@ -32,7 +33,7 @@ export class JsonRpcClient {
#isShutdown: boolean = false;

constructor(
proxyConfig: { targetUrl: string; clientId: string },
proxyConfig: { targetUrl: string; clientId: string; jwkPath: string },
tunnelConfig: {
host: string;
port: number;
Expand All @@ -41,10 +42,11 @@ export class JsonRpcClient {
}
) {
this.#logger = createLogger({ name: "JsonRpcClient" });
const { targetUrl, clientId } = proxyConfig;
const { targetUrl, clientId, jwkPath } = proxyConfig;
this.#targetUrl = targetUrl;
this.#targetHostName = new URL(targetUrl).hostname;
this.#clientId = clientId;
this.#jwkPath = jwkPath;
const { host, port, insecure, backoff } = tunnelConfig;
this.#webSocketUrl = `ws${!insecure ? "s" : ""}://${host}:${port}`;
this.#backoff = backoff;
Expand All @@ -69,7 +71,7 @@ export class JsonRpcClient {
}

async create() {
const token = await jwt(this.#clientId);
const token = await jwt(this.#jwkPath, this.#clientId);
const clientSocket = new WebSocket(this.#webSocketUrl, {
headers: { Authorization: `Bearer ${token}` },
});
Expand Down
22 changes: 11 additions & 11 deletions client/jwks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ const ALG = "ES384"; // Elliptic curve with 384-bit SHA

const logger = pinoLogger({ name: "jwks" });

const loadKey = async () => {
const loadKey = async (path: string) => {
try {
const data = await fs.readFile(privateKeyFile(), {
const data = await fs.readFile(privateKeyFile(path), {
encoding: "utf-8",
});
const key = jose.importJWK(JSON.parse(data));
Expand All @@ -21,18 +21,18 @@ const loadKey = async () => {
}
};

const generateKey = async () => {
const generateKey = async (path: string) => {
const { publicKey, privateKey } = await jose.generateKeyPair(ALG);
await fs.writeFile(
privateKeyFile(),
privateKeyFile(path),
JSON.stringify(await jose.exportJWK(privateKey), undefined, 2),
{
encoding: "utf-8",
}
);
await fs.chmod(privateKeyFile(), "400");
await fs.chmod(privateKeyFile(path), "400");
await fs.writeFile(
publicKeyFile(),
publicKeyFile(path),
JSON.stringify(await jose.exportJWK(publicKey), undefined, 2),
{
encoding: "utf-8",
Expand All @@ -41,14 +41,14 @@ const generateKey = async () => {
return privateKey;
};

const ensureKey = async () => {
const existing = await loadKey();
const ensureKey = async (path: string) => {
const existing = await loadKey(path);
if (existing) return existing;
return await generateKey();
return await generateKey(path);
};

export const jwt = async (clientId: string) => {
const key = await ensureKey();
export const jwt = async (path: string, clientId: string) => {
const key = await ensureKey(path);
const jwt = await new jose.SignJWT({ "tunnel-id": "my-tunnel-id" })
.setProtectedHeader({ alg: ALG })
.setIssuedAt(Math.floor(Date.now() * 1e-3))
Expand Down
6 changes: 5 additions & 1 deletion server/__tests__/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ const runServer = (initContext?: InitContext) => {
const runClient = async (waitUntilConnected?: boolean): Promise<Client> => {
const httpServer = testHttpServer(TARGET_PORT);
const jsonRpcClient = new JsonRpcClient(
{ targetUrl: `http://localhost:${TARGET_PORT}`, clientId: "testClientId" },
{
targetUrl: `http://localhost:${TARGET_PORT}`,
clientId: "testClientId",
jwkPath: "/tmp",
},
{
host: "localhost",
port: SERVER_RPC_PORT,
Expand Down
4 changes: 2 additions & 2 deletions util/jwk-file.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const privateKeyFile = () => `jwk.private.json`;
export const publicKeyFile = () => `jwk.public.json`;
export const privateKeyFile = (path: string) => `${path}/jwk.private.json`;
export const publicKeyFile = (path: string) => `${path}/jwk.public.json`;

0 comments on commit 05a9c34

Please sign in to comment.