when compileOption("threads"): import std/[json, options, tables, unittest] import jwt_full type KeyCache = Table[string, JwkSet] WorkerInput = tuple[keys: KeyCache, passed: ptr bool] proc useKeys(input: WorkerInput) {.thread.} = var keys = input.keys try: for i in 0 ..< 50: let key = keys["issuer"][0] let metadata = key["metadata"].get metadata["labels"][0].str = "worker" var material = key.octKey material.k[0] = '!' # Both metadata and parsed key material remain intact after modifying # accessor results. Each worker also signs and verifies with its copy. if key["metadata"].get["labels"][0].getStr != "original": return if key.octKey.k[0] == '!': return let token = sign(payload = "payload", header = initJoseHeader(%*{"alg": "HS256"}), key = key) token.validate(HS256, key) keys["issuer"].setLen(0) input.passed[] = true except CatchableError: input.passed[] = false suite "jwt_full/jwk thread isolation": test "workers copy key caches and use their keys independently": let node = %*{ "kty": "oct", "alg": "HS256", "kid": "original", "k": "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY", "metadata": {"labels": ["original"]}} let original = {"issuer": @[initJwk(node)]}.toTable var workers: array[4, Thread[WorkerInput]] var passed: array[4, bool] for i in 0 ..< workers.len: createThread(workers[i], useKeys, (original, addr passed[i])) joinThreads(workers) check passed == [true, true, true, true] check original["issuer"].len == 1 check original["issuer"][0]["metadata"].get == node["metadata"] check original["issuer"][0].octKey.k == node["k"].getStr