@@ -0,0 +1,262 @@
4+title: Security Drivers
7+A security driver is a file in `/=home=/sec/<tld>/<domain>/hoon`
8+that handles the authentication for all HTTP requests to
9+`https://<domain>.<tld>`. All requests to the domain are
10+filtered through the security driver, which will either decorate
11+the request with the needed credentials if it has them, or else
12+it will guide the user through the process of authenticating
13+Urbit with the service.
15+Each web service needs its own security driver, but most of them
16+are pretty standard. We recommend starting with an existing
17+security driver based on your needed authentication method (e.g.
18+basic auth, OAuth1, OAuth2), and changing whatever is specific to
21+The best strategy for building security drivers is to copy a
22+similar one and tweak it until it works for you. The best
23+representatives are Github for Basic Authentication, Twitter for
24+OAuth1, Slack for OAuth2 when access tokens don't expire, and
25+Google APIs for OAuth2 when access tokens do expire. In most
26+cases you'll need to make very few changes to one of these
29+Still, it's worth seeing one or two built from the ground up.
30+Here, we'll build a connector for the Github API v3. The
31+simplest way to interact with the Github API is to just fetch
32+https://api.github.com.
34+ +https://api.github.com
36+Github exposes a few endpoints to the general web, and the root
37+endpoint is one of them. This gives you a textual representation
38+of a JSON object that contains a bunch of urls to other parts of
39+Github's API. This is exactly the same response you would get
40+from just running `curl` on UNIX.
42+If we don't have a security driver for Github yet, many of the
43+endpoints won't be accessible, or they will only have publicly
44+accessible information. Most of what we care about requires us
47+Here's a simple security driver:
50+:: Test url +https://api.github.com/user
52+:::: /hoon/github/com/sec
56+|_ {bal/(bale keys:basic-auth) $~}
57+++ aut ~(standard basic-auth bal ~)
58+++ filter-request out-adding-header:aut
62+Github supports authentication through either Basic
63+Authentication or OAuth2. We'll show the basic auth example
64+first, but in general we'd prefer OAuth2.
66+Since this driver is for Github, put it in
67+`/=home=/sec/com/github/hoon`.
69+To try this out, we first have to initialize our credentials with
70+`|init-auth-basic`, which prompts for the url of the service
71+(api.github.com), username, and password. It stores this
72+information in a manner that `%eyre` knows how to decode and send
73+put into your bale. Note that `|init-auth-basic` is standard for
74+basic auth, but other auth schemes (like OAuth) are initialized
77+After you've run `|init-auth-basic`, you should be able to run
78+`+https://api.github.com/user` and get a response indicating who
81+You can similarly POST:
83+ +https://api.github.com/gists &json _(cork poja need)
84+ '{"files":{"file1.txt":{"content":"can\'t stop the signal"}}}'
86+This creates a gist, go to `https://gist.github.com/<username>` to
89+The `+url` syntax used in "source" position makes a GET request,
90+but if you pass it data it will make a POST request with that
91+data as the body. This request is authenticated seamlessly. You
92+can send data of any mark as long as it has a conversion path to
95+Let's take a look at the code and see if we can figure out what's
98+First, we load a library called `basic-auth`. This library
99+exposes two items. `keys` is the type of the basic auth key,
100+which is essentially a base64'd username and password.
101+`standard` accepts a bale and produces a core with everything
102+we'll need to implement basic auth.
104+Every security driver needs some amount of state, which is stored
105+in the bale. The bale contains:
107+- `our`, our urbit name
108+- `now`, the current time
109+- `eny`, 256 bits of entropy
110+- `byk`, the urbit, desk, and case which the security driver is
112+- `usr`, the particular identity we're logging in with
113+- `dom`, the site we're accessing
114+- `key`, the secrets required to authenticate requests. The type
115+ for this entry is supplied by the programmer as the argument to
116+ `++bale`. Thus, in our case, `key` in our bale is of type
119+Additionally, a security driver contains at least one "special"
120+arm. Here, we use `++filter-request`, which is a function which
121+takes a hiss (http request) and produces a `sec-move`, which is
124+- `%send` plus a hiss, which sends the new hiss. This is the
125+ case in, for example, basic auth, where all we need to do is
126+ add an extra header to the request.
127+- `%give` plus an httr (http response), which immediately returns
128+ an http response to the sender.
129+- `%show` plus a purl (parsed url), which displays a message
130+ asking the user to visit the given url to continue the
131+ authentication process.
133+Every request to a Github url will be filtered through
134+`++filter-request`, and we want to add the authentication header
135+to it. Our `++aut` core contains a function
136+`++out-addding-header`, which does exactly what we want.
138+Where do the keys come from, though? Remember we ran
139+`|init-auth-basic` to input them. This just prompts you for the
140+service url, username, and password, and it stores them next to
141+the driver. In our case, that's
142+`/=home=/sec/com/gitub/api/atom`. These keys are stored
143+encrypted, and you don't want to edit them directly. `%eyre`
144+loads them directly into your bale.
148+Most services are better accessed through some form of OAuth.
149+Github can be accessed with OAuth2, which is a little more
150+complicated than basic auth, but not hugely so.
152+The basic steps for OAuth2 are these:
154+- create an app and get its client id and client secret (on
156+- store these in Urbit (with `|init-oauth2`)
157+- try to make a request that requires authentication. The
158+ security driver should prompt you to visit a particular url.
159+- visit the url and click "authorize", which gives the app access
161+- the security driver will turn the code it receives into an
163+- on every request, include that access token
165+Creating an app is easy and
166+[well-documented](https://github.com/settings/applications/new).
167+If necessary, set the callback url to
168+`/~/ac/github.com/_state/in`, and note its client id and client
171+Run `|init-oauth2`, which will prompt for the hostname
172+(api.github.com), client id, and client secret. This will store
173+the information, encrypted, in `/=home=/sec/com/github.atom`,
174+just as with Basic Authentication.
176+Now we need a security driver. Use this:
179+:: Test url +https://api.github.com/user
181+:::: /hoon/github/com/sec
187+|_ {bal/(bale keys:oauth2) tok/token:oauth2}
188+++ scopes :: comment out scopes to taste
189+ :~ 'user' 'user:email' 'user:follow' 'public_repo' 'repo'
190+ 'repo_deployment' 'repo:status' 'delete_repo' 'notifications'
191+ 'gist' 'read:repo_hook' 'write:repo_hook' 'admin:repo_hook'
192+ 'admin:org_hook' 'read:org' 'write:org' 'admin:org'
193+ 'read:public_key' 'write:public_key' 'admin:public_key'
195+:: ++aut is a "standard oauth2" core, which implements the
196+:: most common handling of oauth2 semantics. see lib/oauth2 for more details,
197+:: and examples at the bottom of the file.
198+++ aut (~(standard oauth2 bal tok) . |=(tok/token:oauth2 +>(tok tok)))
200+ %^ out-add-query-param:aut 'access_token'
201+ scope=~[%client %admin]
202+ oauth-dialog='https://github.com/login/oauth/authorize'
204+++ receive-auth-query-string
205+ %- in-code-to-token:aut
206+ url='https://github.com/login/oauth/api/access_token'
207+++ receive-auth-response bak-save-token:aut
211+The oauth2 library provides the main "engine" in `standard`, just
212+like in basic auth, except that we also have to specify a
213+function to save the access token when we get it.
215+Running `+http://api.github.com/user` tries to make a request to
216+Github with authentication. This loads into the bale the keys,
217+which are of type `keys:oauth2`, defined in the oauth2 library.
218+We don't have to worry about the specifics of these keys -- the
219+library handles them -- but they include the client id and the
222+Before the request is sent, `%eyre` calls `++filter-request` to
223+decorate it with authentication headers. Since we don't yet have
224+an access token, we need to prompt the user to visit the "dialog"
225+url. `++out-add-query-param` checks whether we have a token, and
226+if we don't it produces `%show` and a message telling the user to
227+visit the `oauth-dialog` url we provide, with the extra query
228+parameters added (client id, scopes, and state).
230+When the user visits that url, Github will ask them to log in and
231+authorize the application. When they do so, Github will post a
232+request to `/~/ac/github.com/_state/in` with a code in the query
233+string. `%eyre` sends the query string to
234+`++receive-auth-query-string`, which is a function that takes a
235+quay and produces a `sec-move`. `++in-code-to-token` from our
236+`aut` takes the `code` parameter from the quay and uses it to
237+create a request to the given url. This request includes the
238+client id, the client secret, and the code we just got.
240+Github's response to that request includes an access token in its
241+body. This is handled in `++receive-auth-response`.
242+`++bak-save-token` extracts that token and gives it to the
243+function that we passed in the definition of `++aut`. In our
244+case, we just save that token into our state for later usage.
246+Now we have the credentials we need, so `++filter-request` is
247+called with the original request, and `++out-add-query-param`
248+passes the request through with the addition of the query
249+parameter `access_token`.
251+Just like with Basic Authentication, you should be able to run
252+`+https://api.github.com/user` and get a response indicating who
255+Some drivers a slightly more complicated. For example, Github's
256+access token's don't expire, but that's not the case for all
257+service. The googleapis driver shows the flow when the access
258+token can expire. Twitter uses OAuth1, for which we have another
261+Remember, of course, that the best strategy for building security
262+drivers is to copy a similar one and tweak it to taste.