~miikka/clj-branca

clj-branca/doc/user-guide.adoc -rw-r--r-- 3.7 KiB
e063a6abMiikka Koskinen chore(release): prepare for 0.1.2 10 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
== User Guide

https://branca.io/[Branca homepage] describes Branca as follows:

[quote]
____
Branca is a secure easy to use token format which makes it hard to shoot
yourself in the foot. It uses IETF XChaCha20-Poly1305 AEAD symmetric encryption
to create encrypted and tamperproof tokens. The encrypted token is base62
encoded which makes it URL safe. Payload itself is an arbitrary sequence of
bytes.
____

clj-branca is a Clojure implementation of encoding and decoding Branca tokens.
It is built on https://docs.lazycode.co/lazysodium/[Lazysodium] cryptography
library.

=== Installation

Add clj-branca to your project's dependencies:

.deps.edn
[source,clojure]
----
{miikka/clj-branca {:mvn/version "0.1.0"}}
----

.Leiningen
[source,clojure]
----
[miikka/clj-branca "0.1.0"]
----

=== Basic usage

The main namespace of clj-branca is `clj-branca.core`. It is recommended to be
required with the alias `branca`.

[source,clojure]
----
(require '[clj-branca.core :as branca])
----

You can encode payload into a token with `branca/encode`. The payload can be a
byte array, a string, or anything else that can be converted into a byte array
by the https://github.com/aleph-io/byte-streams[byte-streams] library. The
tokens is returned as a string.

[source,clojure]
----
(def secret-key "supersecretkeyyoushouldnotcommit")
(def message "Hello, world!")

(branca/encode secret-key message)
;; "XZ69WpRTqZgEOqCJqaOK4iOKGLkg505VSASQ8MMGWs3mn1p6U81FvB5rSLpKlIjkZTUIBC6KiHIboz"
----

To decode the token, use `branca/decode`. It takes the token as a string
and returns the payload as a byte array.

[source,clojure]
----
(String. (branca/decode secret-key "XZ69WpRTqZgEOqCJqaOK4iOKGLkg505VSASQ8MMGWs3mn1p6U81FvB5rSLpKlIjkZTUIBC6KiHIboz"))
;; "Hello, world!"
----

The encryption key used is 32 bytes. It can be a byte array or anything that can
be converted into a byte array. You can generate a new key with
`clj-branca.crypto/generate-key`.

[source,clojure]
----
(require '[clj-branca.crypto])
(clj-branca.crypto/generate-key)
;; #object["[B" 0x7e00ed0f "[B@7e00ed0f"]
----

=== Token timestamp and time to live

Branca tokens contain a timestamp that indicates when the token was issued. To
limit token lifetime, you can give `:ttl` in seconds in the options map when
calling `branca/decode`. For example, to limit token lifetime to an hour:

[source,clojure]
----
(branca/decode secret-key token {:ttl 3600})
----

If the token is expired, `branca/decode` <<error-handling,throws an exception>>.
If you need to implement more complex TTL logic, you can get the token timestamp
as seconds since the Unix epoch along the payload by calling `branca/decode*`.

[source,clojure]
----
(branca/decode* secret-key "XZ69WpRTqZgEOqCJqaOK4iOKGLkg505VSASQ8MMGWs3mn1p6U81FvB5rSLpKlIjkZTUIBC6KiHIboz")
;; {:version -70,
;;  :timestamp 1602348074,
;;  :nonce #object["[B" 0x37d81587 "[B@37d81587"],
;;  :payload #object["[B" 0x7f3e9acc "[B@7f3e9acc"]}
----

The return value includes the token version and the nonce as well. You probably
won't need them for anything, but they're included for the sake of completeness.

TIP: There is only one Branca version, `0xBA`. This is represented as a
     single-byte value. Because JVM bytes are _signed_, the value
     is printed as `-70` and not as `186` as you might expect.

[[error-handling]]
=== Error handling

Whenever clj-branca encouters an error, such as a malformed or a tampered token,
it throws a `clojure.lang.ExceptionInfo` exception. The exception data contains
the key `:type` to indicate that the exception was thrown by clj-branca.

[options="header""]
|===
| `:type` | explanation
| `:clj-branca.core/invalid-key`    | The key must be a 32-byte byte array.
| `:clj-branca.core/encode-failure` | Encryption failed.
| `:clj-branca.core/invalid-token`  | Decoding token failed.
|===