JWT Implementation for Nim 
This is a implementation of JSON Web Tokens for Nim, it allows for the following operations to be performed:
proc toJWT*(node: JsonNode): JWT - parse a JSON object representing a JWT token to create a JWT token object.
proc toJWT*(s: string): JWT - parse a base64 string to decode it to a JWT token object
sign*(token: var JWT, secret: string) - sign a token. Creates a signature property on the given token and assigns the signature to it.
proc verify*(token: JWT, secret: string, alg: SignatureAlgorithm): bool - verify a token (typically on your incoming requests)
proc $*(token: JWT): string - creates a b64url string from the token
Installation
After installing nim's package manager nimble execute this:
nimble install jwt
Examples
An example to demonstrate use with a userId
import jwt, times, json, tables
var secret = "secret"
proc sign(userId: string): string =
var token = toJWT(%*{
"header": {
"alg": "HS256",
"typ": "JWT"
},
"claims": {
"userId": userId,
"exp": (getTime() + 1.days).toUnix()
}
})
token.sign(secret)
result = $token
proc verify(token: string): bool =
try:
let jwtToken = token.toJWT()
result = jwtToken.verify(secret, HS256)
except InvalidToken:
result = false
proc decode(token: string): string =
let jwt = token.toJWT()
result = $jwt.claims["userId"].node.str
Getting google api oauth2 token:
import jwt, json, times, httpclient, cgi
const email = "username@api-12345-12345.iam.gserviceaccount.com" # Acquired from google api console
const scope = "https://www.googleapis.com/auth/androidpublisher" # Define needed scope
const privateKey = """
-----BEGIN PRIVATE KEY-----
The key should be Acquired from google api console
-----END PRIVATE KEY-----
"""
var tok = initJWT(
header = JOSEHeader(alg: RS256, typ: "JWT"),
claims = toClaims(%*{
"iss": email,
"scope": scope,
"aud": "https://www.googleapis.com/oauth2/v4/token",
"exp": int(epochTime() + 60 * 60),
"iat": int(epochTime())
}))
tok.sign(privateKey)
let postdata = "grant_type=" & encodeUrl("urn:ietf:params:oauth:grant-type:jwt-bearer") & "&assertion=" & $tok
proc request(url: string, body: string): string =
var client = newHttpClient()
client.headers = newHttpHeaders({ "Content-Length": $body.len, "Content-Type": "application/x-www-form-urlencoded" })
result = client.postContent(url, body)
client.close()
let resp = request("https://www.googleapis.com/oauth2/v4/token", postdata).parseJson()
echo "Access token is: ", resp["access_token"].str
Registering in Let's Encrypt's ACME server
let key = "your_rsa_key_here"
let registerAccountPayload = %*{"termsOfServiceAgreed": true}
let resp = makeSignedAcmeRequest(getDirectory()["newAccount"].getStr, registerAccountPayload, key, needsJwk = true)
echo resp.body
proc makeSignedAcmeRequest(
url: string, payload: JsonNode, accountKey: string, needsJwk: bool = false
): Response =
let key = loadRsaKey(accountKey)
var token = toJWT(
%*{
"header":
getAcmeHeader(url, needsJwk, base64UrlEncode(key.n), base64UrlEncode(key.e)),
"claims": payload,
}
)
token.sign(accountKey)
var client = newHttpClient()
let body = token.toFlattennedJson
echo body
client.request(url, httpMethod = HttpPost, body = $body, headers = newHttpHeaders({"Content-Type": "application/jose+json"}))
proc getAcmeHeader(
url: string, needsJwk: bool, n: string = "", e: string = ""
): JsonNode =
var header = %*{"alg": Alg, "typ": "JWT", "nonce": getNewNonce(), "url": url}
if needsJwk:
header["jwk"] = %*{"kty": "RSA", "n": n, "e": e}
else:
header["kid"] = "some_kid"
return header
proc getNewNonce(): string =
let client = newHttpClient()
let nonceURL = getDirectory()["newNonce"].getStr
let resp = client.request(nonceURL, httpMethod = HttpGet)
return resp.headers["replay-nonce"]
proc getDirectory(): JsonNode =
let client = newHttpClient()
let directory = parseJson(client.get(LetsEncryptURL & "/directory").body)
return directory