4 Commits
0.4.2 ... 0.4.6

3 changed files with 26 additions and 9 deletions

View File

@ -1,6 +1,6 @@
# Package # Package
version = "0.4.2" version = "0.4.6"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Jonathan's opinionated extensions and auth layer for Jester." description = "Jonathan's opinionated extensions and auth layer for Jester."
license = "MIT" license = "MIT"

View File

@ -11,6 +11,9 @@ proc newApiError*(parent: ref Exception = nil, respCode: HttpCode, respMsg: stri
result.respCode = respCode result.respCode = respCode
result.respMsg = respMsg result.respMsg = respMsg
if not parent.isNil:
result.trace &= parent.trace
proc raiseApiError*(respCode: HttpCode, respMsg: string, msg = "") = proc raiseApiError*(respCode: HttpCode, respMsg: string, msg = "") =
var apiError = newApiError( var apiError = newApiError(

View File

@ -143,8 +143,15 @@ proc validateJWT*(ctx: ApiAuthContext, jwt: JWT) =
if jwt.claims.sub.isNone: failAuth "Missing 'sub' claim." if jwt.claims.sub.isNone: failAuth "Missing 'sub' claim."
if jwt.claims.exp.isNone: failAuth "Missing or invalid 'exp' claim." if jwt.claims.exp.isNone: failAuth "Missing or invalid 'exp' claim."
if not ctx.validAudiences.contains(jwt.claims.aud.get): if jwt.claims["aud"].get.kind == JString:
failAuth "JWT is not for us (invalid audience)." # If the token is for a single audience, check that it is for us.
if not ctx.validAudiences.contains(jwt.claims.aud.get):
failAuth "JWT is not for us (invalid audience)."
elif jwt.claims["aud"].get.kind == JArray:
# If the token is for multiple audiences, check that at least one is for us.
let auds = jwt.claims["aud"].get.getElems
if not auds.anyIt(ctx.validAudiences.contains(it.getStr)):
failAuth "JWT is not for us (invalid audience)."
let signingAlgorithm = jwt.header.alg.get let signingAlgorithm = jwt.header.alg.get
@ -160,7 +167,7 @@ proc validateJWT*(ctx: ApiAuthContext, jwt: JWT) =
failAuth(getCurrentExceptionMsg(), getCurrentException()) failAuth(getCurrentExceptionMsg(), getCurrentException())
proc extractValidJwt*(ctx: ApiAuthContext, req: Request): JWT = proc extractValidJwt*(ctx: ApiAuthContext, req: Request, validateCsrf = true): JWT =
## Extracts a valid JWT representing the user's authentication and ## Extracts a valid JWT representing the user's authentication and
## authorization details, if present. If there are no valid credentials an ## authorization details, if present. If there are no valid credentials an
## exception is raised. ## exception is raised.
@ -184,6 +191,12 @@ proc extractValidJwt*(ctx: ApiAuthContext, req: Request): JWT =
## ##
## In the split-cookie mode we also check that the `csrfToken` claim in the ## 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. ## JWT payload matches the CSRF value passed via the `X-CSRF-TOKEN` header.
## This CSRF check can be disabled by setting `validateCsrf` to `false`.
## This option is proivded to support occasional use-cases where you want to
## be able to serve a request using cookie auth when the client can't set
## custom headers (e.g. a simple link from an <a> tag). Obviously, this is a
## security risk and should only be used with caution with a full
## understanding of the risk.
try: try:
if req.headers.contains("Authorization"): if req.headers.contains("Authorization"):
@ -207,12 +220,13 @@ proc extractValidJwt*(ctx: ApiAuthContext, req: Request): JWT =
# Because this is a web session, check that the CSRF is present and # Because this is a web session, check that the CSRF is present and
# matches. # matches.
if not req.headers.contains("X-CSRF-TOKEN") or if validateCsrf:
not result.claims["csrfToken"].isSome: if not req.headers.contains("X-CSRF-TOKEN") or
failAuth "missing CSRF token" not result.claims["csrfToken"].isSome:
failAuth "missing CSRF token"
if req.headers["X-CSRF-TOKEN"] != result.claims["csrfToken"].get.getStr(""): if req.headers["X-CSRF-TOKEN"] != result.claims["csrfToken"].get.getStr(""):
failAuth "invalid CSRF token" failAuth "invalid CSRF token"
else: failAuth "no auth token, no Authorization or Cookie headers" else: failAuth "no auth token, no Authorization or Cookie headers"