feat: add toFlattenedJson

This commit is contained in:
Gabriel Cruz
2025-04-15 15:36:00 -03:00
parent 7da158687b
commit 0a59984730
3 changed files with 183 additions and 48 deletions

View File

@@ -17,7 +17,7 @@ This is a implementation of JSON Web Tokens for Nim, it allows for the following
After installing nim's package manager `nimble` execute this:
`nimble install jwt`
## Example
## Examples
An example to demonstrate use with a userId
@@ -90,3 +90,51 @@ proc request(url: string, body: string): string =
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](https://letsencrypt.org/) [ACME](https://www.rfc-editor.org/rfc/rfc8555) server
```nim
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
```

75
jwt.nim
View File

@@ -24,13 +24,15 @@ proc splitToken(s: string): seq[string] =
raise newException(InvalidToken, "Invalid token")
result = parts
proc initJWT*(header: JsonNode, claims: TableRef[string, Claim], signature: seq[byte] = @[]): JWT =
proc initJWT*(
header: JsonNode, claims: TableRef[string, Claim], signature: seq[byte] = @[]
): JWT =
JWT(
headerB64: header.toBase64,
claimsB64: claims.toBase64,
header: header,
claims: claims,
signature: signature
signature: signature,
)
# Load up a b64url string to JWT
@@ -48,7 +50,7 @@ proc toJWT*(s: string): JWT =
claimsB64: claimsB64,
header: headerJson.toHeader(),
claims: claimsJson.toClaims(),
signature: signature
signature: signature,
)
proc toJWT*(node: JsonNode): JWT =
@@ -66,7 +68,9 @@ proc parsed*(token: JWT): string =
result = token.header.toBase64 & "." & token.claims.toBase64
# Signs a string with a secret
proc signString*(toSign: string, secret: string, algorithm: SignatureAlgorithm = HS256): seq[byte] =
proc signString*(
toSign: string, secret: string, algorithm: SignatureAlgorithm = HS256
): seq[byte] =
template hsSign(meth: typed): seq[byte] =
crypto.bearHMAC(addr meth, secret, toSign)
@@ -75,7 +79,7 @@ proc signString*(toSign: string, secret: string, algorithm: SignatureAlgorithm =
template ecSign(hc: typed): seq[byte] =
crypto.bearSignECPem(toSign, secret, addr hc)
case algorithm
of HS256:
return hsSign(sha256Vtable)
@@ -102,25 +106,34 @@ proc signString*(toSign: string, secret: string, algorithm: SignatureAlgorithm =
raise newException(UnsupportedAlgorithm, $algorithm & " isn't supported")
# Verify that the token is not tampered with
proc verifySignature*(data: string, signature: seq[byte], secret: string,
alg: SignatureAlgorithm): bool =
proc verifySignature*(
data: string, signature: seq[byte], secret: string, alg: SignatureAlgorithm
): bool =
case alg
of HS256, HS384, HS512:
let dataSignature = signString(data, secret, alg)
result = dataSignature == signature
of RS256:
result = crypto.bearVerifyRSPem(data, secret, signature, addr sha256Vtable, HASH_OID_SHA256, sha256SIZE)
result = crypto.bearVerifyRSPem(
data, secret, signature, addr sha256Vtable, HASH_OID_SHA256, sha256SIZE
)
of RS384:
result = crypto.bearVerifyRSPem(data, secret, signature, addr sha384Vtable, HASH_OID_SHA384, sha384SIZE)
result = crypto.bearVerifyRSPem(
data, secret, signature, addr sha384Vtable, HASH_OID_SHA384, sha384SIZE
)
of RS512:
result = crypto.bearVerifyRSPem(data, secret, signature, addr sha512Vtable, HASH_OID_SHA512, sha512SIZE)
result = crypto.bearVerifyRSPem(
data, secret, signature, addr sha512Vtable, HASH_OID_SHA512, sha512SIZE
)
of ES256:
result = crypto.bearVerifyECPem(data, secret, signature, addr sha256Vtable, sha256SIZE)
result =
crypto.bearVerifyECPem(data, secret, signature, addr sha256Vtable, sha256SIZE)
of ES384:
result = crypto.bearVerifyECPem(data, secret, signature, addr sha384Vtable, sha384SIZE)
result =
crypto.bearVerifyECPem(data, secret, signature, addr sha384Vtable, sha384SIZE)
of ES512:
result = crypto.bearVerifyECPem(data, secret, signature, addr sha512Vtable, sha512SIZE)
result =
crypto.bearVerifyECPem(data, secret, signature, addr sha512Vtable, sha512SIZE)
else:
assert(false, "Not implemented")
@@ -130,16 +143,44 @@ proc sign*(token: var JWT, secret: string) =
# Verify a token typically an incoming request
proc verify*(token: JWT, secret: string, alg: SignatureAlgorithm): bool =
token.header.alg == alg and verifySignature(token.loaded, token.signature, secret, alg)
token.header.alg == alg and verifySignature(
token.loaded, token.signature, secret, alg
)
proc toString*(token: JWT): string =
token.header.toBase64 & "." & token.claims.toBase64 & "." & token.signatureToB64
proc toFlattenedJson*(token: JWT, unprotectedHeader: bool = false): JsonNode =
# Converts JWT to flattened JSON format
# https://datatracker.ietf.org/doc/html/rfc7515#section-7.2.2
if not unprotectedHeader:
return
%*{
"payload": token.claims.toBase64,
"protected": token.header.toBase64,
"signature": token.signatureToB64,
}
# protected field is the base64url of "typ" and "alg"
let protected = %*{"typ": token.header["typ"], "alg": token.header["alg"]}
# (unprotected) header field contains all other header keys and not on base64url
var header = %*{}
for key in token.header.keys:
if not protected.hasKey(key):
header[key] = token.header[key]
return
%*{
"payload": token.claims.toBase64,
"protected": protected.toBase64,
"header": header,
"signature": token.signatureToB64,
}
proc `$`*(token: JWT): string =
token.toString
proc `%`*(token: JWT): JsonNode =
let s = $token
%s
@@ -153,7 +194,7 @@ proc verifyTimeClaims*(token: JWT) =
if token.claims.hasKey("exp"):
let exp = token.claims["exp"].getClaimTime
if now > exp :
if now > exp:
raise newException(InvalidToken, "Token is expired")
# Verify token nbf exp

View File

@@ -10,10 +10,8 @@ proc getToken(claims: JsonNode = newJObject(), header: JsonNode = newJObject()):
initJWT(header.toHeader, claims.toClaims)
proc tokenWithAlg(alg: string): JWT =
let header = %*{ "typ": "JWT", "alg": alg }
let claims = %*{ "sub": "1234567890",
"name": "John Doe",
"iat": 1516239022 }
let header = %*{"typ": "JWT", "alg": alg}
let claims = %*{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
initJWT(header.toHeader, claims.toClaims)
proc signedHSToken(alg: string): JWT =
@@ -21,7 +19,8 @@ proc signedHSToken(alg: string): JWT =
result.sign("your-256-secret")
const
rsPrivateKey = """-----BEGIN RSA PRIVATE KEY-----
rsPrivateKey =
"""-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWw
kWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mr
m/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEi
@@ -48,7 +47,8 @@ NvVi5vcba9oGdElJX3e9mxqUKMrw7msJJv1MX8LWyMQC5L6YNYHDfbPF1q5L4i8j
y18Ae9n7dHVueyslrb6weq7dTkYDi3iOYRW8HRkIQh06wEdbxt0shTzAJvvCQfrB
jg/3747WSsf/zBTcHihTRBdAv6OmdhV4/dD5YBfLAkLrd+mX7iE=
-----END RSA PRIVATE KEY-----"""
rsPublicKey = """-----BEGIN PUBLIC KEY-----
rsPublicKey =
"""-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv
vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc
aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy
@@ -57,28 +57,33 @@ e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb
V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9
MwIDAQAB
-----END PUBLIC KEY-----"""
ec256PrivKey = """-----BEGIN PRIVATE KEY-----
ec256PrivKey =
"""-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2
OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r
1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G
-----END PRIVATE KEY-----"""
ec256PubKey = """-----BEGIN PUBLIC KEY-----
ec256PubKey =
"""-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9
q9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==
-----END PUBLIC KEY-----"""
ec384PrivKey = """-----BEGIN EC PRIVATE KEY-----
ec384PrivKey =
"""-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDCAHpFQ62QnGCEvYh/pE9QmR1C9aLcDItRbslbmhen/h1tt8AyMhske
enT+rAyyPhGgBwYFK4EEACKhZANiAAQLW5ZJePZzMIPAxMtZXkEWbDF0zo9f2n4+
T1h/2sh/fviblc/VTyrv10GEtIi5qiOy85Pf1RRw8lE5IPUWpgu553SteKigiKLU
PeNpbqmYZUkWGh3MLfVzLmx85ii2vMU=
-----END EC PRIVATE KEY-----"""
ec384PubKey = """-----BEGIN PUBLIC KEY-----
ec384PubKey =
"""-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEC1uWSXj2czCDwMTLWV5BFmwxdM6PX9p+
Pk9Yf9rIf374m5XP1U8q79dBhLSIuaojsvOT39UUcPJROSD1FqYLued0rXiooIii
1D3jaW6pmGVJFhodzC31cy5sfOYotrzF
-----END PUBLIC KEY-----"""
ec512PrivKey = """-----BEGIN EC PRIVATE KEY-----
ec512PrivKey =
"""-----BEGIN EC PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBiyAa7aRHFDCh2qga
9sTUGINE5jHAFnmM8xWeT/uni5I4tNqhV5Xx0pDrmCV9mbroFtfEa0XVfKuMAxxf
Z6LM/yKhgYkDgYYABAGBzgdnP798FsLuWYTDDQA7c0r3BVk8NnRUSexpQUsRilPN
@@ -86,14 +91,14 @@ v3SchO0lRw9Ru86x1khnVDx+duq4BiDFcvlSAcyjLACJvjvoyTLJiA+TQFdmrear
jMiZNE25pT2yWP1NUndJxPcvVtfBW48kPOmvkY4WlqP5bAwCXwbsKrCgk6xbsp12
ew==
-----END EC PRIVATE KEY-----"""
ec512PubKey = """-----BEGIN PUBLIC KEY-----
ec512PubKey =
"""-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBgc4HZz+/fBbC7lmEww0AO3NK9wVZ
PDZ0VEnsaUFLEYpTzb90nITtJUcPUbvOsdZIZ1Q8fnbquAYgxXL5UgHMoywAib47
6MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj+WwM
Al8G7CqwoJOsW7Kddns=
-----END PUBLIC KEY-----"""
proc signedRSToken(alg: string): JWT =
result = tokenWithAlg(alg)
result.sign(rsPrivateKey)
@@ -132,16 +137,22 @@ suite "Token tests":
test "HS Signature":
# Checked with https://jwt.io/
check:
$signedHSToken("HS256") == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.sBnEuqpBDTh4Q9wnxfhWKHPbbspoz-qPNxXqVSS7ZYE"
$signedHSToken("HS384") == "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.e-lF0-wO2pi5y5fCOHPFLTuHqm2hR1LIX3gaCz0xI_Nvw-KPNIpkKVcbxWl2pPz8"
$signedHSToken("HS512") == "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.oAFx4658Y0Bbjko7Vm-X1AUd4XRjvnuznZk8cihzDuIRSZQjXnveoKuj8PIkAWviz-5c--R1HSyM6HZuONtrLQ"
$signedHSToken("HS256") ==
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.sBnEuqpBDTh4Q9wnxfhWKHPbbspoz-qPNxXqVSS7ZYE"
$signedHSToken("HS384") ==
"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.e-lF0-wO2pi5y5fCOHPFLTuHqm2hR1LIX3gaCz0xI_Nvw-KPNIpkKVcbxWl2pPz8"
$signedHSToken("HS512") ==
"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.oAFx4658Y0Bbjko7Vm-X1AUd4XRjvnuznZk8cihzDuIRSZQjXnveoKuj8PIkAWviz-5c--R1HSyM6HZuONtrLQ"
test "RS Signature":
# Checked with https://jwt.io/
check:
$signedRSToken("RS256") == "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.O2LIRo2GPEVHQCG3nHGvvY89__LgKLPo9EYXLDzH3oQnh_hZvlk350htpqaNMowOlxYGdM77oLsdHVxzFdto9c1pCH0jBG-HXzIKm131QxsZzCyO8ovW_2i6PGeNvsiaggrkdmOKcWcyMksasJcuqIf0h_fWhiK4wdq41Ls8ujLJpQBF3XNzOPt90so7XEvkY0zDVS0N3Bi6Hz5cN101FJFyMcDnq_3QSGMWPy829vC8PT8C0WCBIs7VdK9tEwIvpDENhRRj6cxhUqLCC0ALoynZYBeMcvOWQcz-LqbWuQGvuH2HGsN9zCpbaTdkiupNX__DKG0HUijnesYn1DkY2g"
$signedRSToken("RS384") == "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.OGwjm7YvGCh4gpIuFnM7K88_dEeiSAWzpR0dXzhsne1IPygnXRKoTCdmhA2a01Mj_cW6tWhufSGcuu-7vmdm5Hi8hoDe5Q92kmM44oWikKptCy_zIM_Roe30TPjXxweE_WjV1fMZaAX6UFumikrtWCTcb9rLnSjpHYFgo-buS7cBXg_nK7xgOPz-bQvv8edVWsBWPf92B9Mak-LNZla_F5EAOjXrN16ZQ1y4qE94ro051kryqUddfVonmLSjCrCavttfBMugYf-SCbLp0w_QLaT9gA_bMXVzqyLnIj74Sr_JCWAxcYU5RaFmqZLEpowyp-m9XGdBwVS2118K0TooZg"
$signedRSToken("RS512") == "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.dgb3ak_0nwQyLa2Ssmq3Jok-pr9QfVFnw_63YlFXTkq_V8r816VeCOzBRYvVv6ONvKGZDR_3SAqf3UJp1XkXtN-VyJ7VRoSHZ0d0-3DPArxDrIu20uvoQrbm4LqQtwbGPH-B-Z-7Bvfng-iwhOt1S717AepZsgVjQz2gOvBvzFsg_BDZ6nhU-5GOnIRkJ2amUt5N1TXbzKHkNLtMpKlq1BZbdv_xKSHgw_IHQRl9lIIQs_2_NuTgk8nQQiwtb9L1v3Y3KYpYGCBvgohWDcpyUKOv5f2EHekDpj1f_ALltd8gzWhIDgwK5VbBo8JAkLWDRfeTOS0fh0Faenfn551wqA"
$signedRSToken("RS256") ==
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.O2LIRo2GPEVHQCG3nHGvvY89__LgKLPo9EYXLDzH3oQnh_hZvlk350htpqaNMowOlxYGdM77oLsdHVxzFdto9c1pCH0jBG-HXzIKm131QxsZzCyO8ovW_2i6PGeNvsiaggrkdmOKcWcyMksasJcuqIf0h_fWhiK4wdq41Ls8ujLJpQBF3XNzOPt90so7XEvkY0zDVS0N3Bi6Hz5cN101FJFyMcDnq_3QSGMWPy829vC8PT8C0WCBIs7VdK9tEwIvpDENhRRj6cxhUqLCC0ALoynZYBeMcvOWQcz-LqbWuQGvuH2HGsN9zCpbaTdkiupNX__DKG0HUijnesYn1DkY2g"
$signedRSToken("RS384") ==
"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.OGwjm7YvGCh4gpIuFnM7K88_dEeiSAWzpR0dXzhsne1IPygnXRKoTCdmhA2a01Mj_cW6tWhufSGcuu-7vmdm5Hi8hoDe5Q92kmM44oWikKptCy_zIM_Roe30TPjXxweE_WjV1fMZaAX6UFumikrtWCTcb9rLnSjpHYFgo-buS7cBXg_nK7xgOPz-bQvv8edVWsBWPf92B9Mak-LNZla_F5EAOjXrN16ZQ1y4qE94ro051kryqUddfVonmLSjCrCavttfBMugYf-SCbLp0w_QLaT9gA_bMXVzqyLnIj74Sr_JCWAxcYU5RaFmqZLEpowyp-m9XGdBwVS2118K0TooZg"
$signedRSToken("RS512") ==
"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.dgb3ak_0nwQyLa2Ssmq3Jok-pr9QfVFnw_63YlFXTkq_V8r816VeCOzBRYvVv6ONvKGZDR_3SAqf3UJp1XkXtN-VyJ7VRoSHZ0d0-3DPArxDrIu20uvoQrbm4LqQtwbGPH-B-Z-7Bvfng-iwhOt1S717AepZsgVjQz2gOvBvzFsg_BDZ6nhU-5GOnIRkJ2amUt5N1TXbzKHkNLtMpKlq1BZbdv_xKSHgw_IHQRl9lIIQs_2_NuTgk8nQQiwtb9L1v3Y3KYpYGCBvgohWDcpyUKOv5f2EHekDpj1f_ALltd8gzWhIDgwK5VbBo8JAkLWDRfeTOS0fh0Faenfn551wqA"
signedRSToken("RS256").verify(rsPublicKey, RS256)
signedRSToken("RS384").verify(rsPublicKey, RS384)
@@ -150,8 +161,10 @@ suite "Token tests":
test "EC Signature":
# Checked with https://jwt.io/
check:
signedECToken("ES256", ec256PrivKey).header.toBase64 == "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"
signedECToken("ES256", ec256PrivKey).claims.toBase64 == "eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ"
signedECToken("ES256", ec256PrivKey).header.toBase64 ==
"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"
signedECToken("ES256", ec256PrivKey).claims.toBase64 ==
"eyJuYW1lIjoiSm9obiBEb2UiLCJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ"
# We don't check signatures, as for ES* algorithms they are random
@@ -160,17 +173,50 @@ suite "Token tests":
signedECToken("ES512", ec512PrivKey).verify(ec512PubKey, ES512)
test "header values":
var token = toJWT(%*{
"header": {
"alg": "HS256",
"kid": "something",
"typ": "JWT"
},
"claims": {
"userId": 1
var token = toJWT(
%*{
"header": {"alg": "HS256", "kid": "something", "typ": "JWT"},
"claims": {"userId": 1},
}
})
)
token.sign(rsPrivateKey)
let signed = $token
let decoded = signed.toJWT()
check decoded.header["kid"].getStr() == "something"
test "toFlaflattenedJson":
var token = toJWT(
%*{
"header": {"alg": "HS256", "kid": "something", "typ": "JWT"},
"claims": {"userId": 1},
}
)
token.sign(rsPrivateKey)
let expectedFlattened =
%*{
"payload": "eyJ1c2VySWQiOjF9",
"protected": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbWV0aGluZyJ9",
"signature": "JlHgw86VQ7xgOn1ACnwqjXfU28CHD_9GrCMu9JO0rr4",
}
check expectedFlattened == token.toFlattenedJson
test "toFlaflattenedJson with unprotectedHeader":
var token = toJWT(
%*{
"header": {"alg": "HS256", "kid": "something", "typ": "JWT"},
"claims": {"userId": 1},
}
)
token.sign(rsPrivateKey)
let expectedFlattenedUnprotected =
%*{
"payload": "eyJ1c2VySWQiOjF9",
"protected": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9",
"header": {"kid": "something"},
"signature": "JlHgw86VQ7xgOn1ACnwqjXfU28CHD_9GrCMu9JO0rr4",
}
check expectedFlattenedUnprotected == token.toFlattenedJson(
unprotectedHeader = true
)