1 module ddata.jwt.encode;
2 
3 import std.json;
4 import std.base64;
5 
6 import ddata.jwt.algorithm;
7 import ddata.jwt.sign;
8 
9 /**
10     Encodes a jwt with the current payload and specified algorithm
11 */
12 public string encode(const JSONValue payload, string key, Algorithm algorithm = Algorithm.hs256, JSONValue header = null) @safe {
13     return encode(cast(ubyte[]) payload.toString().dup, key, algorithm, header);
14 }
15 
16 private string encode(const(ubyte)[] payload, string key, Algorithm algorithm = Algorithm.hs256, JSONValue header = null) @safe {
17     if (header.type == JSONType.null_) {
18         header = (JSONValue[string]).init;
19     }
20     header["alg"] = cast(string) algorithm;
21     header["typ"] = "JWT";
22     const headerString = header.toString;
23 
24     const encodedHeader = Base64URLNoPadding.encode(cast(ubyte[])headerString.dup);
25     const encodedPayload = Base64URLNoPadding.encode(payload);
26 
27     const signingInput = encodedHeader ~ "." ~ encodedPayload;
28     const signatureInput = sign(signingInput.idup, key, algorithm);
29     const signature = Base64URLNoPadding.encode(cast(ubyte[])signatureInput.dup);
30 
31     return (signingInput ~ "." ~ signature).idup;
32 }
33 
34 @("should encode json data in to jwt with private rsa key")
35 @safe unittest {
36     immutable key = q"EOS
37 -----BEGIN RSA PRIVATE KEY-----
38 MIIEpQIBAAKCAQEAx80KNM7esDMCGwcLkJfvhLKBZtE1WuS+Q49jNSRDJt/nR2jS
39 0X3lkrT5rTR2l3JQiQ2a7X5mXznOiv/zgs07/wxnBFiCm93NTgaoP/iyRt7oiuN4
40 lwnQd4x8VlPPFt8+FEAF8LjmesqOFxvb2utXhnm72gBiV0KVcT8OJ880jyyn/sx+
41 lm2KAQE1dtDLPv2iR+oHeX34pHdaF2Pn5LvmkYLutFUB/jWbhUS6G5grns5QvCPF
42 7iI9KvplOn9n/cGNjGuu+686Sa4eWR96jsTi3ubvZpPio13rgaPpj3bp0NiQgOn5
43 OtPFiE/tqBuJRFnRL8rKsJeIV2RriqtyyPX/qQIDAQABAoIBAQCQw2LtuCVpDMwt
44 wQEEFtGYF63iTRqXbLzePnnm+wsck4YDG1QELW+0yCNO94AlYtIvOwhRow+RV1Tr
45 KV/KGeGqfdX2NBsNy7sBGZm2H/8rkj5ywzWQWbANrmA4PCkDrWRRT8H+FDoKJdCl
46 ta2qBHI6IOGWpkiaaMfWcZVUCrFOOgcJwz0FGUiH++gfRyOTiXmTd2VK6Yx8K3o5
47 PyETxOFb5Ct4OUvhWerxz6VW5s1wE9rUMjinTgkhBFNK0ya+Tilihq8sPWRq3C75
48 kBsW8gKOE9quOWYHslXHIEmhCdf36SoZPEzb3GLHuPPvXtMo2PG7EU7SqnnBw9qU
49 IBi2qlIBAoGBAPzVFjGAn1oQeP7j5ATW2bf/hmLFaDZtazO2d38K1MIBm0IftYPv
50 /33QM9GtnkGbApD7bKB28TgZ4tz/RxVu0T4CAU1Xnk+HMZZvaIUlMXQayhZMudFs
51 UCql1t0vZYV+7DinODlEBYu0J9Gxs+Xtu+GvsP9wGJIkpHOB8tn5vurZAoGBAMpN
52 3VcyihNhZtnvNMmKd2NNJl9m8+IOuIdld2fzjg0S0Z3Oadv6pGCKB5MlsGWLzOXb
53 sBGCdJ/NSIFFNouyu2Q3zI70gEalwieogbBuOmHOEBPPy/DRetfybumP2/KvCRQk
54 9yKfsnqWnHSB35dknLbIsbtbylnqC4gGVaE/gplRAoGATxEYWqy9qL3ECPodocHK
55 3nbDgPn5KCQ5xTdH0WwCsxUrh5dA/Zy9Sowk2GqyNhQSzmJCS0BHGWNHBhOzGCnK
56 t9iKrbQ75uUBhekbR6AlAgkqr6SY67wyqdOpCQy8c+4IU4M/2vDBxzm0xigLeVlK
57 Sz7VXFyi4nkhEJpP5wjqQqkCgYEAuXAASNJ5wGQS7AeZInh7ERoB54cuXHNT8EAw
58 4Kde+Vrbq8QuQscP77H2WYo9lAc+jR//1zz2fBimsl/oLMtre+St7AfwoVGFk+ag
59 4kFX4JkLIa3i6d6KtPFze0IzwdNyBfYQVrt91WLDDQSTGGnQQLfcOrpb1Gl1onzz
60 9veJVdECgYEA3/YfFzB7MjWxTtmlgyv5VdOuLLxkw0KV7QhoOv5zbwp9GsJFJWxN
61 OE6Trrq1EYQRe/NqdIRNt9tJMxLV+0E8s8kvT2yQPHvOWxV6lq0O5ypFeBT7wnXp
62 aLVVG9//pbbfefHaMZ4A98GT1b7e08Q/QX89cTUORvaQCM7E7QNqLoo=
63 -----END RSA PRIVATE KEY-----
64 EOS";
65 
66     immutable expectedJwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9."
67         ~ "eyJleHAiOjYwMCwiaWF0IjowLCJpc3MiOjExMTF9.WSERqrEL5JSoNO-"
68         ~ "B_G6wt0pGFPYIN3WfTbjMZQEZHznLMkWPqGoQL_iWcF2QHKKVxLUxJTm"
69         ~ "fONccSodJ2j6zQMlmdbU9LXa-DzwZTUYLLP6GiQrFv1IgTiFngjJQ32z"
70         ~ "L6I2SIwMFiPBxyFImku_xrsT2MZu9J6N-VyPqM0LJ6nYPsKso-Nlo-iw"
71         ~ "2PRh01YF5_rrxT8q45lOvHkflyZogESb8BaiJMZqscbey1TmDQq1TgiD"
72         ~ "HRIHzuB3SBQa5E2s24cs9VSIOrnJJeUuWiVTYQrY9c5nwR_xT2W_rHsT"
73         ~ "Pp2sEVePvIQJOfzu8iTraveKF4U0IliUI_wNYytiOVHnovg";
74 
75     const payload = `{"iat": 0, "exp": 600, "iss": 1111}`;
76     const json = parseJSON(payload);
77     const jwt = encode(json, key, Algorithm.rs256);
78 
79     assert(jwt == expectedJwt);
80 }