Facilitate http code response on websocket JWT authentication failure (#1043)

* Facilitate http code response on websocket JWT authentication failure

* Update JSON-RPC link
This commit is contained in:
Jordan Hrycaj 2022-04-07 10:37:35 +01:00 committed by GitHub
parent 737236fd6e
commit 8af5c33ef9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 75 deletions

View File

@ -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:

View File

@ -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

View File

@ -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()

2
vendor/nim-json-rpc vendored

@ -1 +1 @@
Subproject commit d4ae2328d4247c59cefd8d5e0fbc3f178a0eb4ef
Subproject commit b80313bfed0594198d77c6a22616f1f96c0d91fa