Update version of namespaced_logging.

This commit is contained in:
Jonathan Bernard 2023-02-04 00:24:25 -06:00
parent daa78f974a
commit 3d2f52ee1d
3 changed files with 22 additions and 21 deletions

View File

@ -1,6 +1,6 @@
# Package
version = "0.2.1"
version = "0.2.2"
author = "Jonathan Bernard"
description = "JDB Software's opinionated extensions and auth layer for Jester."
license = "MIT"
@ -12,8 +12,8 @@ srcDir = "src"
requires "nim >= 1.6.2"
requires @["bcrypt", "jester >= 0.5.0", "uuids"]
requires "https://git.jdb-software.com/jdb/nim-jwt-full.git"
requires "https://git.jdb-software.com/jdb/nim-namespaced-logging.git"
requires "https://git.jdb-software.com/jdb/nim-jwt-full.git >= 0.2.0"
requires "https://git.jdb-software.com/jdb/nim-namespaced-logging.git >= 0.3.0"
task unittest, "Runs the unit test suite.":
exec "nim c -r test/runner"

View File

@ -8,7 +8,7 @@ const CONTENT_TYPE_JSON* = "application/json"
var logNs {.threadvar.}: LoggingNamespace
template log(): untyped =
if logNs.isNil: logNs = initLoggingNamespace("buffoonery/apiutils", lvlDebug)
if logNs.isNil: logNs = getLoggerForNamespace("buffoonery/apiutils", lvlDebug)
logNs
## Response Utilities

View File

@ -28,7 +28,7 @@ type
var logNs {.threadvar.}: LoggingNamespace
template log(): untyped =
if logNs.isNil: logNs = initLoggingNamespace("buffoonery/auth", lvlDebug)
if logNs.isNil: logNs = getLoggerForNamespace("buffoonery/auth", lvlDebug)
logNs
proc failAuth*(reason: string, parentException: ref Exception = nil) =
@ -91,6 +91,11 @@ proc addSigningKeys*(ctx: ApiAuthContext, issuer: string, keySet: JwkSet): void
proc findSigningKey*(ctx: ApiAuthContext, jwt: JWT, allowFetch = true): JWK {.gcsafe.} =
## Lookup the signing key for a given JWT. This method assumes that you trust
## the issuer named in the JWT.
##
## If our ApiAuthContext does not contain any keys for the issuer named in
## the JWT, or if that set of keys does not contain the key id referenced in
## the JWT, we will attempt to fetch the signing keys based on the
## [OpenID Connect standard discovery mechanism](https://openid.net/specs/openid-connect-discovery-1_0.html)
try:
if jwt.claims.iss.isNone: failAuth "Missing 'iss' claim."
@ -100,23 +105,20 @@ proc findSigningKey*(ctx: ApiAuthContext, jwt: JWT, allowFetch = true): JWK {.gc
let jwtIssuer = jwt.claims.iss.get
# Do we already have keys for this issuer in our cache?
if ctx.issuerKeys.hasKey(jwtIssuer):
# Do we have the key for this keyId?
let foundKeys = ctx.issuerKeys[jwtIssuer]
.filterIt(it.kid.isSome and it.kid.get == jwt.header.kid.get)
if foundKeys.len == 1: return foundKeys[0]
# If all of the above were true, we should have returned. If we reach this
# point, we know that one of the above was false and we need to refresh our
# cache of keys.
# If we didn't have keys for that issuer, or if we couldn't find the given
# kid, we need to refresh our cache of keys.
if allowFetch:
ctx.issuerKeys[jwtIssuer] =
fetchJWKs(jwtIssuer & "/.well-known/openid-configuration")
return ctx.findSigningKey(jwt, false)
else: failAuth "unable to find JWT signing key"
failAuth "unable to find JWT signing key"
except:
log.error "unable to find JWT signing key: " & getCurrentExceptionMsg()
@ -165,24 +167,22 @@ proc extractValidJwt*(ctx: ApiAuthContext, req: Request): JWT =
## We support two authentication flows:
##
## - Strict API via a JWT Bearer token in the Authorization header. This is
## intended for API consumers (not the browser-based web-app). In this
## case, the token is validated directly.
## intended for API consumers (not a browser-based web-app). In this case,
## the token is validated directly.
##
## - Split JWT via two cookies:
##
## - `${cookiePrefix}-user`: Contains the JWT header and payload, but not the
## signature. This cookie is set Secure. The JWT payload contains a 30
## minute expiry (and the Max-Age is set the same) and also contains a
## CSRF token. This cookie is accessible by the web application.
## signature. This cookie should be set Secure. The JWT payload should
## have a defined expiration date (matching the Max-Age of the cookie)
## and a CSRF token. This cookie is accessible by the web application.
##
## - `${cookiePrefix}-session`: Contains the JWT signature. This cookie is
## set Secure and HttpOnly. This serves as the session token (when the
## user closes the browser this gets unset).
##
## In this split-cookie mode, the API will also check for the presence of a
## CSRF token on any mutation requests (PUT, POST, and DELETE requests).
## The client must set the X-CSRF-TOKEN header with the same CSRF value
## present in the `csrfToken` claim in the JWT presented in the
## `${cookiePrefix}-user` cookie.
## In the split-cookie mode we also check that the `csrfToken` claim in the
## JWT payload matches the CSRF value passed via the `X-CSRF-TOKEN` header.
try:
if headers(req).hasKey("Authorization"):
@ -238,6 +238,7 @@ proc createSignedJWT*(ctx: ApiAuthContext, claims: JsonNode, kid: string): JWT =
sigKey)
proc newApiAccessToken*(ctx: ApiAuthContext, sub: string, duration = 1.hours): JWT =
## Create a new JWT for API access.
result = ctx.createSignedJWT(
%*{
"sub": sub,