Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
semyenov committed Mar 15, 2024
1 parent e185595 commit f70e74a
Show file tree
Hide file tree
Showing 16 changed files with 222 additions and 63 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# App
keys/*.jwk

# Nuxt
.nuxt
.nitro
Expand Down
4 changes: 4 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@
"christian-kohler.path-intellisense",
"Codeium.codeium",
"cpylua.language-postcss",
"cweijan.vscode-redis-client",
"dbaeumer.vscode-eslint",
"eamodio.gitlens",
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"fagnercarvalho.redis-lsp",
"formulahendry.auto-rename-tag",
"foxundermoon.shell-format",
"helixquar.randomeverything",
"heybourn.headwind",
"howardzuo.vscode-npm-dependency",
"IBM.output-colorizer",
"idered.npm",
"kisstkondoros.vscode-codemetrics",
"lacroixdavid1.vscode-format-context-menu",
"lokalise.i18n-ally",
Expand All @@ -40,6 +43,7 @@
"micnil.vscode-checkpoints",
"mikestead.dotenv",
"mkxml.vscode-filesize",
"mongodb.mongodb-vscode",
"ms-vscode.vscode-js-profile-flame",
"Nuxt.mdc",
"oderwat.indent-rainbow",
Expand Down
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,8 @@
},
"[shellscript]": {
"editor.defaultFormatter": "foxundermoon.shell-format"
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
}
}
32 changes: 16 additions & 16 deletions defs/TestUserInfo.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "TestUserInfo",
"title": "Test User Info",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string"
}
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "TestUserInfo",
"title": "Test User Info",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"required": [
"name",
"email"
]
}
"email": {
"type": "string"
}
},
"required": [
"name",
"email"
]
}
Empty file added keys/.gitkeep
Empty file.
4 changes: 2 additions & 2 deletions lib/bullmq/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Queue } from 'bullmq'

export const bullmq = new Queue<
{ message: string },
{ status: number }
{ message: string },
{ status: number }
>('appQueue',
{
connection: {
Expand Down
15 changes: 8 additions & 7 deletions lib/guard/t1.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import consola from 'consola'
import * as jose from 'jose'

const alg = 'ES256'

const keys = await jose.generateKeyPair(alg, { extractable: true })
const keys2 = await jose.generateKeyPair(alg, { extractable: true })

console.log('keys\n', await jose.exportPKCS8(keys.privateKey))
console.log('keys2\n', await jose.exportPKCS8(keys2.privateKey))
consola.log('keys\n', await jose.exportPKCS8(keys.privateKey))
consola.log('keys2\n', await jose.exportPKCS8(keys2.privateKey))

const jwt = await new jose.GeneralSign(
const jws = await new jose.GeneralSign(
new TextEncoder().encode('Hello, World!'),
)
.addSignature(keys.privateKey)
Expand All @@ -17,12 +18,12 @@ const jwt = await new jose.GeneralSign(
.setProtectedHeader({ alg, b64: true })
.sign()

console.log('jwt', jwt)
consola.log('jws', jws)

const { payload, protectedHeader } = await jose.generalVerify(
jwt,
jws,
keys2.publicKey,
)

console.log(protectedHeader)
console.log(new TextDecoder().decode(payload))
consola.log(protectedHeader)
consola.log(new TextDecoder().decode(payload))
13 changes: 8 additions & 5 deletions lib/guard/t2.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import consola from 'consola'
import * as jose from 'jose'

const alg = 'ES256'
Expand All @@ -10,6 +11,8 @@ const keys = await jose.generateKeyPair(alg, { extractable: true })
const keys2 = await jose.generateKeyPair(alg, { extractable: true })
// const keys3 = await jose.generateKeyPair(alg, { extractable: true });

consola.info(keys.publicKey)

const jwk1 = await jose.exportJWK(keys.publicKey).then((jwk) => {
jwk.kid = 'key1'

Expand All @@ -29,8 +32,8 @@ const jwk2 = await jose.exportJWK(keys2.publicKey).then((jwk) => {

const jwks = jose.createLocalJWKSet({ keys: [jwk1, jwk2] })

console.log('keys\n', await jose.exportPKCS8(keys.privateKey))
console.log('keys2\n', await jose.exportPKCS8(keys2.privateKey))
consola.log('keys\n', await jose.exportPKCS8(keys.privateKey))
consola.log('keys2\n', await jose.exportPKCS8(keys2.privateKey))

const jwt = await new jose.SignJWT({
foo: 'bar',
Expand All @@ -42,10 +45,10 @@ const jwt = await new jose.SignJWT({
.setIssuedAt()
.sign(keys.privateKey)

console.log('jwt', jwt)
consola.log('jwt', jwt)

const { payload, protectedHeader } = await jose
.jwtVerify(jwt, jwks, options)

console.log(protectedHeader)
console.log(payload)
consola.log(protectedHeader)
consola.log(payload)
24 changes: 24 additions & 0 deletions lib/jose/keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { readFile } from 'node:fs/promises'

import * as jose from 'jose'

import type { KeyPair } from './types'

export const keys1: KeyPair = JSON.parse(await readFile(
'keys/key1.jwk',
'utf8',
))
export const keys2: KeyPair = JSON.parse(await readFile(
'keys/key2.jwk',
'utf8',
))

export const keys1Private = await jose.importJWK(keys1.privateKey)
export const keys2Private = await jose.importJWK(keys2.privateKey)

export const jwks = jose.createLocalJWKSet({
keys: [
keys1.publicKey,
keys2.publicKey,
],
})
41 changes: 41 additions & 0 deletions lib/jose/sign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as jose from 'jose'

import { keys1Private } from './keys'

import type { KeyPair } from './types'

const alg = 'ES256'
const options = {
issuer: 'urn:example:issuer',
audience: 'urn:example:audience',
}

export async function sign(
keyPair: KeyPair,
payload: jose.JWTPayload,
) {
return new jose.SignJWT(payload)
.setIssuer(options.issuer)
.setAudience(options.audience)
.setProtectedHeader({
alg,
kid: 'key1',
})
.setExpirationTime('10m')
.setIssuedAt()
.sign(keys1Private)
}

export async function verify(
jwt: string,
keySet: jose.JWTVerifyGetKey,
verifyOptions?: jose.VerifyOptions,
) {
const { payload } = await jose.jwtVerify(
jwt,
keySet,
verifyOptions,
)

return payload
}
5 changes: 5 additions & 0 deletions lib/jose/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type * as jose from 'jose'

export type KeyPair = jose.GenerateKeyPairResult<
jose.KeyLike & jose.JWK
>
24 changes: 14 additions & 10 deletions lib/ws/client.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { sleep } from '@antfu/utils'
import consola from 'consola'
import { WebSocket } from 'ws'

import { jwks, keys1 } from '@/jose/keys'
import { sign, verify } from '@/jose/sign'

import type { Buffer } from 'node:buffer'
import type { ClientOptions } from 'ws'

const logger = consola.withTag('client/ws')
// import consola from 'consola'
// const logger = consola.withTag('client/ws')

type BufferLike =
| string
Expand Down Expand Up @@ -54,10 +56,10 @@ export class WebSocketProxy extends WebSocket {
) {
this.on(event, async (...args: any[]) => {
if (event === 'message') {
await sleep(100)
logger.debug('Receiving', event, JSON.parse(args[0] as string))
const [data, isBinary] = args as [BufferLike, boolean]
listener.call(this, data, isBinary)

const jws = await verify(data.toString(), jwks)
listener.call(this, JSON.stringify(jws), isBinary)

return
}
Expand All @@ -66,10 +68,12 @@ export class WebSocketProxy extends WebSocket {
})
}

private async customSend(data: BufferLike, cb?: (error?: Error) => void) {
await sleep(100)
logger.debug('Sending', data)
private async customSend(
data: BufferLike,
cb?: (error?: Error) => void,
) {
const jws = await sign(keys1, JSON.parse(data.toString()))

this.send(data, cb)
this.send(jws, cb)
}
}
31 changes: 15 additions & 16 deletions lib/ws/server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { sleep } from '@antfu/utils'
import consola from 'consola'
import { WebSocketServer } from 'ws'

import { jwks, keys2 } from '@/jose/keys'
import { sign, verify } from '@/jose/sign'

import type { Buffer } from 'node:buffer'
import type { IncomingMessage } from 'node:http'
import type { ServerOptions, WebSocket } from 'ws'
Expand All @@ -19,12 +21,12 @@ type BufferLike =
| SharedArrayBuffer
| readonly any[]
| readonly number[]
| { valueOf(): ArrayBuffer }
| { valueOf(): SharedArrayBuffer }
| { valueOf(): Uint8Array }
| { valueOf(): readonly number[] }
| { valueOf(): string }
| { [Symbol.toPrimitive](hint: string): string }
| { valueOf: () => ArrayBuffer }
| { valueOf: () => SharedArrayBuffer }
| { valueOf: () => Uint8Array }
| { valueOf: () => readonly number[] }
| { valueOf: () => string }
| { [Symbol.toPrimitive]: (hint: string) => string }

export class WebSocketProxy<
T extends typeof WebSocket.WebSocket = typeof WebSocket.WebSocket,
Expand Down Expand Up @@ -84,11 +86,10 @@ export class WebSocketProxy<
}

if (event === 'message') {
await sleep(100)
logger.info('Receiving', event, JSON.parse(args[0] as string))
// logger.debug('Receiving', event, JSON.parse(args[0] as string))
const [data, isBinary] = args as [BufferLike, boolean]
listener.call(this, data, isBinary)
const jws = await verify(data.toString(), jwks)

listener.call(this, JSON.stringify(jws), isBinary)

return
}
Expand All @@ -102,12 +103,10 @@ export class WebSocketProxy<
data: BufferLike,
cb?: (error?: Error) => void,
) {
await sleep(100)

logger.debug('Sending', data)

if ('send' in this) {
return this.send(data, cb)
const jws = await sign(keys2, JSON.parse(data.toString()))

return this.send(jws, cb)
}
}
}
36 changes: 36 additions & 0 deletions scripts/keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { writeFile } from 'node:fs/promises'

import * as jose from 'jose'

const alg = 'ES256'

const keys1 = await jose.generateKeyPair(alg, { extractable: true })
const keys2 = await jose.generateKeyPair(alg, { extractable: true })

const jwk1 = {
publicKey: await jose.exportJWK(keys1.publicKey).then(addKid('key1')),
privateKey: await jose.exportJWK(keys1.privateKey).then(addKid('key1')),
}

const jwk2 = {
publicKey: await jose.exportJWK(keys2.publicKey).then(addKid('key2')),
privateKey: await jose.exportJWK(keys2.privateKey).then(addKid('key2')),
}

await writeFile(
`keys/key1.jwk`,
JSON.stringify(jwk1, null, 2),
)

await writeFile(
`keys/key2.jwk`,
JSON.stringify(jwk2, null, 2),
)

function addKid(kid: string) {
return (jwk: jose.JWK) => {
jwk.kid = kid

return jwk
}
}
Loading

0 comments on commit f70e74a

Please sign in to comment.