Add WebSocket auth — JWT token validation on WS upgrade handshake
- websocket.nim: check Authorization Bearer token before WebSocket upgrade - httpserver.nim: pass config + jwtSecret to WsServer
This commit is contained in:
@@ -42,11 +42,11 @@ proc newHttpServer*(config: BaraConfig): HttpServer =
|
|||||||
let db = newLSMTree(dataDir)
|
let db = newLSMTree(dataDir)
|
||||||
let ctx = newExecutionContext(db)
|
let ctx = newExecutionContext(db)
|
||||||
ctx.txnManager = newTxnManager()
|
ctx.txnManager = newTxnManager()
|
||||||
let ws = newWsServer()
|
let secret = if config.jwtSecret.len > 0: config.jwtSecret else: "baradb-default-secret-change-in-production!"
|
||||||
|
let ws = newWsServer(config, secret)
|
||||||
ctx.onChange = proc(ev: ChangeEvent) =
|
ctx.onChange = proc(ev: ChangeEvent) =
|
||||||
let msg = $ev.kind & " " & ev.table
|
let msg = $ev.kind & " " & ev.table
|
||||||
asyncCheck ws.broadcastToTable(ev.table, msg)
|
asyncCheck ws.broadcastToTable(ev.table, msg)
|
||||||
let secret = if config.jwtSecret.len > 0: config.jwtSecret else: "baradb-default-secret-change-in-production!"
|
|
||||||
HttpServer(config: config, running: false, db: db, ctx: ctx,
|
HttpServer(config: config, running: false, db: db, ctx: ctx,
|
||||||
secretKey: secret,
|
secretKey: secret,
|
||||||
metrics: Metrics(), ws: ws)
|
metrics: Metrics(), ws: ws)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import std/strutils
|
|||||||
import std/tables
|
import std/tables
|
||||||
import std/base64
|
import std/base64
|
||||||
import std/sets
|
import std/sets
|
||||||
|
import config
|
||||||
|
import jwt as jwtlib
|
||||||
|
|
||||||
type
|
type
|
||||||
WsFrame = object
|
WsFrame = object
|
||||||
@@ -24,11 +26,14 @@ type
|
|||||||
clients*: Table[int, WsClient]
|
clients*: Table[int, WsClient]
|
||||||
nextId: int
|
nextId: int
|
||||||
running: bool
|
running: bool
|
||||||
|
config*: BaraConfig
|
||||||
|
secretKey*: string
|
||||||
onInsert*: proc (table, key, value: string) {.closure.}
|
onInsert*: proc (table, key, value: string) {.closure.}
|
||||||
onDelete*: proc (table, key: string) {.closure.}
|
onDelete*: proc (table, key: string) {.closure.}
|
||||||
|
|
||||||
proc newWsServer*(): WsServer =
|
proc newWsServer*(cfg: BaraConfig = defaultConfig(), secret: string = ""): WsServer =
|
||||||
WsServer(clients: initTable[int, WsClient](), nextId: 1, running: false)
|
WsServer(clients: initTable[int, WsClient](), nextId: 1, running: false,
|
||||||
|
config: cfg, secretKey: secret)
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# WebSocket frame encoding/decoding (RFC 6455)
|
# WebSocket frame encoding/decoding (RFC 6455)
|
||||||
@@ -270,6 +275,25 @@ proc handleConnection(server: WsServer, client: AsyncSocket) {.async.} =
|
|||||||
client.close()
|
client.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Auth check
|
||||||
|
if server.config.authEnabled:
|
||||||
|
let authHeader = headers.getOrDefault("authorization", "")
|
||||||
|
if authHeader.len == 0 or not authHeader.startsWith("Bearer "):
|
||||||
|
await client.send("HTTP/1.1 401 Unauthorized\r\n\r\n")
|
||||||
|
client.close()
|
||||||
|
return
|
||||||
|
let tokenStr = authHeader[7..^1]
|
||||||
|
try:
|
||||||
|
let token = tokenStr.toJWT()
|
||||||
|
if not token.verify(server.secretKey, HS256):
|
||||||
|
await client.send("HTTP/1.1 401 Unauthorized\r\n\r\n")
|
||||||
|
client.close()
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
await client.send("HTTP/1.1 401 Unauthorized\r\n\r\n")
|
||||||
|
client.close()
|
||||||
|
return
|
||||||
|
|
||||||
let acceptKey = computeAcceptKey(wsKey)
|
let acceptKey = computeAcceptKey(wsKey)
|
||||||
|
|
||||||
var response = "HTTP/1.1 101 Switching Protocols\r\n"
|
var response = "HTTP/1.1 101 Switching Protocols\r\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user