From 8af5c33ef9f06429ab599342ae0a50efc7a02b45 Mon Sep 17 00:00:00 2001 From: Jordan Hrycaj Date: Thu, 7 Apr 2022 10:37:35 +0100 Subject: [PATCH] Facilitate http code response on websocket JWT authentication failure (#1043) * Facilitate http code response on websocket JWT authentication failure * Update JSON-RPC link --- nimbus/nimbus.nim | 2 +- nimbus/rpc/jwt_auth.nim | 90 ++++++++++------------------------------- tests/test_jwt_auth.nim | 8 ++-- vendor/nim-json-rpc | 2 +- 4 files changed, 27 insertions(+), 75 deletions(-) diff --git a/nimbus/nimbus.nim b/nimbus/nimbus.nim index 432f01191..8f1f9089c 100644 --- a/nimbus/nimbus.nim +++ b/nimbus/nimbus.nim @@ -190,7 +190,7 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf, msg = $(rc.unsafeError) # avoid side effects quit(QuitFailure) # Authentcation handler constructor - @[rc.value.jwtAuthAsyHook] + some(rc.value.jwtAuthHandler) # Creating Websocket RPC Server if conf.wsEnabled: diff --git a/nimbus/rpc/jwt_auth.nim b/nimbus/rpc/jwt_auth.nim index b13468500..50e01877c 100644 --- a/nimbus/rpc/jwt_auth.nim +++ b/nimbus/rpc/jwt_auth.nim @@ -18,6 +18,7 @@ import chronicles, chronos, chronos/apps/http/httptable, + json_rpc/servers/websocketserver, httputils, websock/types as ws, nimcrypto/[hmac, utils], @@ -39,19 +40,9 @@ const 32 type - # -- currently unused -- - # - #JwtAuthHandler* = ##\ - # ## JSW authenticator prototype - # proc(req: HttpTable): Result[void,(HttpCode,string)] - # {.gcsafe, raises: [Defect].} - # - - JwtAuthAsyHandler* = ##\ - ## Asynchroneous JSW authenticator prototype. This is the definition - ## appicable for the `verify` entry of a `ws.Hook`. - proc(req: HttpTable): Future[Result[void,string]] - {.closure, gcsafe, raises: [Defect].} + JwtAuthHandler* = ##\ + ## Generic authentication handler, also provided by the web-socket server. + RpcWebSocketServerAuth JwtSharedKey* = ##\ ## Convenience type, needed quite often @@ -269,68 +260,29 @@ proc jwtSharedSecret*(rng: ref BrHmacDrbgContext; config: NimbusConf): result = rng.jwtGenSecret.jwtSharedSecret(config) -# -- currently unused -- -# -#proc jwtAuthHandler*(key: JwtSharedKey): JwtAuthHandler = -# ## Returns a JWT authentication handler that can be used with an HTTP header -# ## based call back system. -# ## -# ## The argument `key` is captured by the session handler for JWT -# ## authentication. The function `jwtSharedSecret()` provides such a key. -# result = proc(req: HttpTable): Result[void,(HttpCode,string)] = -# let auth = req.getString("Authorization","?") -# if auth.len < 9 or auth[0..6].cmpIgnoreCase("Bearer ") != 0: -# return err((Http403, "Missing Token")) -# -# let rc = auth[7..^1].strip.verifyTokenHS256(key) -# if rc.isOk: -# return ok() -# -# debug "Could not authenticate", -# error = rc.error -# -# case rc.error: -# of jwtTokenValidationError, jwtMethodUnsupported: -# return err((Http401, "Unauthorized")) -# else: -# return err((Http403, "Malformed Token")) -# - -proc jwtAuthAsyHandler*(key: JwtSharedKey): JwtAuthAsyHandler = - ## Returns an asynchroneous JWT authentication handler that can be used with - ## an HTTP header based call back system. +proc jwtAuthHandler*(key: JwtSharedKey): JwtAuthHandler = + ## Returns a JWT authentication handler that can be used with an HTTP header + ## based call back system as the web socket server. ## ## The argument `key` is captured by the session handler for JWT ## authentication. The function `jwtSharedSecret()` provides such a key. - result = proc(req: HttpTable): Future[Result[void,string]] {.async.} = - let auth = req.getString("Authorization","?") - if auth.len < 9 or auth[0..6].cmpIgnoreCase("Bearer ") != 0: - return err("Missing Token") + result = proc(req: HttpTable): Result[void,(HttpCode,string)] {.gcsafe.} = + let auth = req.getString("Authorization","?") + if auth.len < 9 or auth[0..6].cmpIgnoreCase("Bearer ") != 0: + return err((Http403, "Missing Token")) - let rc = auth[7..^1].strip.verifyTokenHS256(key) - if rc.isOk: - return ok() + let rc = auth[7..^1].strip.verifyTokenHS256(key) + if rc.isOk: + return ok() - debug "Could not authenticate", - error = rc.error + debug "Could not authenticate", + error = rc.error - case rc.error: - of jwtTokenValidationError, jwtMethodUnsupported: - return err("Unauthorized") - else: - return err("Malformed Token") - -proc jwtAuthAsyHook*(key: JwtSharedKey): ws.Hook = - ## Variant of `jwtAuthHandler()` (e.g. directly suitable for Json WebSockets.) - ## - ## Note that currently there is no meaningful way to send a http 401/403 in - ## case of an authentication problem. - let handler = key.jwtAuthAsyHandler - ws.Hook( - append: proc(ctx: ws.Hook, req: var HttpTable): Result[void,string] = - ok(), - verify: proc(ctx: ws.Hook, req: HttpTable): Future[Result[void,string]] = - req.handler) + case rc.error: + of jwtTokenValidationError, jwtMethodUnsupported: + return err((Http401, "Unauthorized")) + else: + return err((Http403, "Malformed Token")) # ------------------------------------------------------------------------------ # End diff --git a/tests/test_jwt_auth.nim b/tests/test_jwt_auth.nim index e699900c9..4ce56cff6 100644 --- a/tests/test_jwt_auth.nim +++ b/tests/test_jwt_auth.nim @@ -224,7 +224,7 @@ proc runJwtAuth(noisy = true; keyFile = jwtKeyFile) = secret = fakeKey.fakeGenSecret.jwtSharedSecret(config) # The wrapper contains the handler function with the captured shared key - asyHandler = secret.value.jwtAuthAsyHandler + handler = secret.value.jwtAuthHandler suite "EngineAuth: Http/rpc authentication mechanics": @@ -249,7 +249,7 @@ proc runJwtAuth(noisy = true; keyFile = jwtKeyFile) = setTraceLevel() # Run http authorisation request - let htCode = waitFor req.asyHandler + let htCode = req.handler noisy.say "***", "result", " htCode=", htCode @@ -268,7 +268,7 @@ proc runJwtAuth(noisy = true; keyFile = jwtKeyFile) = setTraceLevel() # Run http authorisation request - let htCode = waitFor req.asyHandler + let htCode = req.handler noisy.say "***", "result", " htCode=", htCode @@ -285,7 +285,7 @@ proc jwtAuthMain*(noisy = defined(debug)) = when isMainModule: const - noisy = defined(debug) or true + noisy = defined(debug) setErrorLevel() diff --git a/vendor/nim-json-rpc b/vendor/nim-json-rpc index d4ae2328d..b80313bfe 160000 --- a/vendor/nim-json-rpc +++ b/vendor/nim-json-rpc @@ -1 +1 @@ -Subproject commit d4ae2328d4247c59cefd8d5e0fbc3f178a0eb4ef +Subproject commit b80313bfed0594198d77c6a22616f1f96c0d91fa