Make API auth contexts safe to copy across threads
This commit is contained in:
1 parent
5e05be6d0e
commit
e86100176c
5 files changed
+163
-13
No files matched your search
+10
-10
@@ -13,7 +13,10 @@ type
|
||||
AuthError* = object of CatchableError
|
||||
additionalInfo*: Option[TableRef[string, JsonNode]]
|
||||
|
||||
ApiAuthContext* = ref object
|
||||
ApiAuthContext* = object
|
||||
## An owned authentication configuration and signing-key cache. Copy before
|
||||
## handing to a worker; each worker's cache updates stay local to its copy.
|
||||
## Concurrent access to the same mutable context still needs synchronization.
|
||||
appDomain*: string ## Application domain for session cookies
|
||||
cookiePrefix*: string ## Prefix for the user and session cookies
|
||||
validAudiences*: seq[string] ## Expected audience values for for `aud` JWT check
|
||||
@@ -26,7 +29,7 @@ type
|
||||
## must be provided either when the ApiAuthContext is initialized (see
|
||||
## `initApiAuthContext` or via `addSigningKeys`
|
||||
|
||||
issuerKeys: TableRef[string, JwkSet]
|
||||
issuerKeys: Table[string, JwkSet]
|
||||
|
||||
|
||||
proc failAuth*[T](
|
||||
@@ -71,7 +74,7 @@ proc initApiAuthContext*(
|
||||
issuer: issuer,
|
||||
trustedIssuers: trustedIssuers,
|
||||
signingKid: signingKid,
|
||||
issuerKeys: newTable[string, JwkSet]([(issuer, signingKeys)]))
|
||||
issuerKeys: toTable([(issuer, signingKeys)]))
|
||||
|
||||
|
||||
proc fetchJWKs(openIdConfigUrl: string): JwkSet {.gcsafe.} =
|
||||
@@ -101,17 +104,16 @@ proc fetchJWKs(openIdConfigUrl: string): JwkSet {.gcsafe.} =
|
||||
parentException = getCurrentException())
|
||||
|
||||
|
||||
proc addSigningKeys*(ctx: ApiAuthContext, issuer: string, keySet: JwkSet): void =
|
||||
proc addSigningKeys*(ctx: var ApiAuthContext, issuer: string, keySet: JwkSet): void =
|
||||
## Manually add a set of signing keys associated with a given issuer.
|
||||
try:
|
||||
for k in keySet: validateSigningKey(k)
|
||||
if ctx.issuerKeys.isNil: ctx.issuerKeys = newTable[string, JwkSet]()
|
||||
ctx.issuerKeys[issuer] = keySet
|
||||
except:
|
||||
raise getCurrentException()
|
||||
|
||||
|
||||
proc findSigningKey*(ctx: ApiAuthContext, jwt: JWT, allowFetch = true): JWK {.gcsafe.} =
|
||||
proc findSigningKey*(ctx: var 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.
|
||||
##
|
||||
@@ -124,8 +126,6 @@ proc findSigningKey*(ctx: ApiAuthContext, jwt: JWT, allowFetch = true): JWK {.gc
|
||||
if jwt.claims.iss.isNone: failAuth "JWT is missing 'iss' claim."
|
||||
if jwt.header.kid.isNone: failAuth "JWT is missing 'kid' header."
|
||||
|
||||
if ctx.issuerKeys.isNil: ctx.issuerKeys = newTable[string, JwkSet]()
|
||||
|
||||
let jwtIssuer = jwt.claims.iss.get
|
||||
|
||||
if ctx.issuerKeys.hasKey(jwtIssuer):
|
||||
@@ -151,7 +151,7 @@ proc findSigningKey*(ctx: ApiAuthContext, jwt: JWT, allowFetch = true): JWK {.gc
|
||||
failAuth("unable to find JWT signing key", getCurrentException())
|
||||
|
||||
|
||||
proc validateJWT*(ctx: ApiAuthContext, jwt: JWT) =
|
||||
proc validateJWT*(ctx: var ApiAuthContext, jwt: JWT) =
|
||||
## Given a JWT, validate that it is a well-formed JWT, validate the issuer's
|
||||
## signature on the token, and validate all the claims that it preesnts.
|
||||
try:
|
||||
@@ -195,7 +195,7 @@ proc validateJWT*(ctx: ApiAuthContext, jwt: JWT) =
|
||||
failAuth(getCurrentExceptionMsg(), getCurrentException())
|
||||
|
||||
|
||||
proc extractValidJwt*(ctx: ApiAuthContext, req: Request, validateCsrf = true): JWT =
|
||||
proc extractValidJwt*(ctx: var ApiAuthContext, req: Request, validateCsrf = true): JWT =
|
||||
## Extracts a valid JWT representing the user's authentication and
|
||||
## authorization details, if present. If there are no valid credentials an
|
||||
## exception is raised.
|
||||
|
||||
Reference in new issue
Block a user