mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-29 03:52:38 +02:00
333 lines
No EOL
19 KiB
HTML
333 lines
No EOL
19 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
|
|
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
|
|
|
|
<title>What's Hawk and how to use it? - Alexis - Carnets en ligne</title>
|
|
|
|
<meta charset="utf-8" />
|
|
<link href="https://blog.notmyidea.org/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Alexis - Carnets en ligne Full Atom Feed" />
|
|
<link rel="stylesheet" href="https://blog.notmyidea.org/theme/css/poole.css"/>
|
|
<link rel="stylesheet" href="https://blog.notmyidea.org/theme/css/syntax.css"/>
|
|
<link rel="stylesheet" href="https://blog.notmyidea.org/theme/css/lanyon.css"/>
|
|
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=PT+Serif:400,400italic,700%7CPT+Sans:400">
|
|
<link rel="stylesheet" href="https://blog.notmyidea.org/theme/css/styles.css"/>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
h1 {
|
|
font-family: "Avant Garde", Avantgarde, "Century Gothic", CenturyGothic, "AppleGothic", sans-serif;
|
|
padding: 80px 50px;
|
|
text-align: center;
|
|
text-transform: uppercase;
|
|
text-rendering: optimizeLegibility;
|
|
color: #202020;
|
|
letter-spacing: .1em;
|
|
text-shadow:
|
|
-1px -1px 1px #111,
|
|
2px 2px 1px #eaeaea;
|
|
}
|
|
|
|
#main {
|
|
text-align: justify;
|
|
text-justify: inter-word;
|
|
}
|
|
#main h1 {
|
|
padding: 10px;
|
|
}
|
|
|
|
.post-headline {
|
|
padding: 15px;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<!-- Target for toggling the sidebar `.sidebar-checkbox` is for regular
|
|
styles, `#sidebar-checkbox` for behavior. -->
|
|
<input type="checkbox" class="sidebar-checkbox" id="sidebar-checkbox">
|
|
<!-- Toggleable sidebar -->
|
|
<div class="sidebar" id="sidebar">
|
|
<div class="sidebar-item">
|
|
<div class="profile">
|
|
<img src="https://blog.notmyidea.org/theme/img/profile.png"/>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="sidebar-nav">
|
|
<a class="sidebar-nav-item" href="/">Articles</a>
|
|
|
|
<a class="sidebar-nav-item" href="https://www.vieuxsinge.com">Brasserie du Vieux Singe</a>
|
|
<a class="sidebar-nav-item" href="http://blog.notmyidea.org/pages/about.html">A propos</a>
|
|
<a class="sidebar-nav-item" href="https://twitter.com/ametaireau">Messages courts</a>
|
|
<a class="sidebar-nav-item" href="https://github.com/almet">Code</a>
|
|
</nav>
|
|
</div> <div class="wrap">
|
|
<div class="masthead">
|
|
<div class="container">
|
|
<h3 class="masthead-title">
|
|
<a href="https://blog.notmyidea.org/" title="Home">Alexis - Carnets en ligne</a>
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="container content">
|
|
<div id="main" class="posts">
|
|
<h1 class="post-title">What's Hawk and how to use it?</h1>
|
|
<span class="post-date">31 juillet 2014</span>
|
|
<img id="illustration" src="" />
|
|
|
|
<div class="post article">
|
|
<h1>🌟</h1>
|
|
|
|
<p>At Mozilla, we recently had to implement <a href="https://github.com/hueniverse/hawk">the Hawk authentication
|
|
scheme</a> for a number of projects,
|
|
and we came up creating two libraries to ease integration into pyramid
|
|
and node.js apps.</p>
|
|
<p>But maybe you don't know Hawk.</p>
|
|
<p>Hawk is a relatively new technology, crafted by one of the original
|
|
<a href="https://en.wikipedia.org/wiki/OAuth">OAuth</a> specification authors, that
|
|
intends to replace the 2-legged OAuth authentication scheme using a
|
|
simpler approach.</p>
|
|
<p>It is an authentication scheme for HTTP, built around <a href="https://en.wikipedia.org/wiki/Hmac">HMAC
|
|
digests</a> of requests and responses.</p>
|
|
<p>Every authenticated client request has an Authorization header
|
|
containing a MAC (Message Authentication Code) and some additional
|
|
metadata, then each server response to authenticated requests contains a
|
|
Server-Authorization header that authenticates the response, so the
|
|
client is sure it comes from the right server.</p>
|
|
<h2 id="exchange-of-the-hawk-id-and-hawk-key">Exchange of the hawk id and hawk key</h2>
|
|
<p>To sign the requests, a client needs to retrieve a token id and a token
|
|
key from the server.</p>
|
|
<p>Hawk itself does not define how these credentials should be exchanged
|
|
between the server and the client. The excellent team behind <a href="http://accounts.firefox.com">Firefox
|
|
Accounts</a> put together a scheme to do that,
|
|
which acts like the following:</p>
|
|
<div class="note">
|
|
|
|
<div class="admonition-title">
|
|
|
|
Note
|
|
|
|
</div>
|
|
|
|
All this derivation crazyness might seem a bit complicated, but don't
|
|
worry, we put together some libraries that takes care of that for you
|
|
automatically.
|
|
|
|
If you are not interested into these details, you can directly jump to
|
|
the next section to see how to use the libraries.
|
|
|
|
</div>
|
|
|
|
<p>When your server application needs to send you the credentials, it will
|
|
return it inside a specific Hawk-Session-Token header. This token can be
|
|
derived to split this string in two values (hawk id and hawk key) that
|
|
you will use to sign your next requests.</p>
|
|
<p>In order to get the hawk credentials, you'll need to:</p>
|
|
<p>First, do an <a href="http://en.wikipedia.org/wiki/HKDF">HKDF derivation</a> on the
|
|
given session token. You'll need to use the following
|
|
parameters:</p>
|
|
<div class="highlight"><pre><span></span><span class="n">key_material</span> <span class="o">=</span> <span class="n">HKDF</span><span class="p">(</span><span class="n">hawk_session</span><span class="p">,</span> <span class="ss">""</span><span class="p">,</span> <span class="s1">'identity.mozilla.com/picl/v1/sessionToken'</span><span class="p">,</span> <span class="mi">32</span><span class="o">*</span><span class="mi">2</span><span class="p">)</span>
|
|
</pre></div>
|
|
|
|
|
|
<div class="note">
|
|
|
|
<div class="admonition-title">
|
|
|
|
Note
|
|
|
|
</div>
|
|
|
|
The `identity.mozilla.com/picl/v1/sessionToken` is a reference to this
|
|
way of deriving the credentials, not an actual URL.
|
|
|
|
</div>
|
|
|
|
<p>Then, the key material you'll get out of the HKDF need to be separated
|
|
into two parts, the first 32 hex caracters are the hawk id, and the next
|
|
32 ones are the hawk key.</p>
|
|
<p>Credentials:</p>
|
|
<p>``` sourceCode javascript
|
|
credentials = {
|
|
'id': keyMaterial[0:32],
|
|
'key': keyMaterial[32:64],
|
|
'algorithm': 'sha256'
|
|
}</p>
|
|
<div class="highlight"><pre><span></span><span class="c1">## Httpie</span>
|
|
|
|
<span class="n">To</span> <span class="n">showcase</span> <span class="n">APIs</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">documentation</span><span class="p">,</span> <span class="n">I</span> <span class="n">like</span> <span class="n">to</span> <span class="n">use</span>
|
|
<span class="p">[</span><span class="n">httpie</span><span class="p">](</span><span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">jakubroztocil</span><span class="o">/</span><span class="n">httpie</span><span class="p">),</span> <span class="n">a</span> <span class="n">curl</span><span class="o">-</span><span class="n">replacement</span>
|
|
<span class="k">with</span> <span class="n">a</span> <span class="n">nicer</span> <span class="n">API</span><span class="p">,</span> <span class="n">built</span> <span class="n">around</span> <span class="p">[</span><span class="n">the</span> <span class="n">python</span> <span class="n">requests</span>
|
|
<span class="n">library</span><span class="p">](</span><span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">python</span><span class="o">-</span><span class="n">requests</span><span class="o">.</span><span class="n">org</span><span class="p">)</span><span class="o">.</span>
|
|
|
|
<span class="n">Luckily</span><span class="p">,</span> <span class="n">HTTPie</span> <span class="n">allows</span> <span class="n">you</span> <span class="n">to</span> <span class="n">plug</span> <span class="n">different</span> <span class="n">authentication</span> <span class="n">schemes</span> <span class="k">for</span>
|
|
<span class="n">it</span><span class="p">,</span> <span class="n">so</span> <span class="p">[</span><span class="n">I</span> <span class="n">wrote</span> <span class="n">a</span>
|
|
<span class="n">wrapper</span><span class="p">](</span><span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">mozilla</span><span class="o">-</span><span class="n">services</span><span class="o">/</span><span class="n">requests</span><span class="o">-</span><span class="n">hawk</span><span class="p">)</span> <span class="n">around</span>
|
|
<span class="p">[</span><span class="n">mohawk</span><span class="p">](</span><span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">kumar303</span><span class="o">/</span><span class="n">mohawk</span><span class="p">)</span> <span class="n">to</span> <span class="n">add</span> <span class="n">hawk</span> <span class="n">support</span> <span class="n">to</span> <span class="n">the</span>
|
|
<span class="n">requests</span> <span class="n">lib</span><span class="o">.</span>
|
|
|
|
<span class="n">Doing</span> <span class="n">hawk</span> <span class="n">requests</span> <span class="ow">in</span> <span class="n">your</span> <span class="n">terminal</span> <span class="ow">is</span> <span class="n">now</span> <span class="k">as</span> <span class="n">simple</span> <span class="k">as</span><span class="p">:</span>
|
|
|
|
<span class="err">$</span> <span class="n">pip</span> <span class="n">install</span> <span class="n">requests</span><span class="o">-</span><span class="n">hawk</span> <span class="n">httpie</span>
|
|
<span class="err">$</span> <span class="n">http</span> <span class="n">GET</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">5000</span><span class="o">/</span><span class="n">registration</span> <span class="o">--</span><span class="n">auth</span><span class="o">-</span><span class="nb">type</span><span class="o">=</span><span class="n">hawk</span> <span class="o">--</span><span class="n">auth</span><span class="o">=</span><span class="s1">'id:key'</span>
|
|
|
|
<span class="n">In</span> <span class="n">addition</span><span class="p">,</span> <span class="n">it</span> <span class="n">will</span> <span class="n">help</span> <span class="n">you</span> <span class="n">to</span> <span class="n">craft</span> <span class="n">requests</span> <span class="n">using</span> <span class="n">the</span> <span class="n">requests</span>
|
|
<span class="n">library</span><span class="p">:</span>
|
|
|
|
<span class="sb">``</span><span class="err">`</span> <span class="n">sourceCode</span> <span class="n">python</span>
|
|
<span class="kn">import</span> <span class="nn">requests</span>
|
|
<span class="kn">from</span> <span class="nn">requests_hawk</span> <span class="kn">import</span> <span class="n">HawkAuth</span>
|
|
|
|
<span class="n">hawk_auth</span> <span class="o">=</span> <span class="n">HawkAuth</span><span class="p">(</span>
|
|
<span class="n">credentials</span><span class="o">=</span><span class="p">{</span><span class="s1">'id'</span><span class="p">:</span> <span class="nb">id</span><span class="p">,</span> <span class="s1">'key'</span><span class="p">:</span> <span class="n">key</span><span class="p">,</span> <span class="s1">'algorithm'</span><span class="p">:</span> <span class="s1">'sha256'</span><span class="p">})</span>
|
|
|
|
<span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"/url"</span><span class="p">,</span> <span class="n">auth</span><span class="o">=</span><span class="n">hawk_auth</span><span class="p">)</span>
|
|
</pre></div>
|
|
|
|
|
|
<p>Alternatively, if you don't have the token id and key, you can pass the
|
|
hawk session token I talked about earlier and the lib will take care of
|
|
the derivation for you:</p>
|
|
<p>``` sourceCode python
|
|
hawk_auth = HawkAuth(
|
|
hawk_session=resp.headers['hawk-session-token'],
|
|
server_url=self.server_url
|
|
)
|
|
requests.post("/url", auth=hawk_auth)</p>
|
|
<div class="highlight"><pre><span></span><span class="c1">## Integrate with python pyramid apps</span>
|
|
|
|
<span class="n">If</span> <span class="n">you</span><span class="s1">'re writing pyramid applications, you'</span><span class="n">ll</span> <span class="n">be</span> <span class="n">happy</span> <span class="n">to</span> <span class="n">learn</span> <span class="n">that</span>
|
|
<span class="p">[</span><span class="n">Ryan</span> <span class="n">Kelly</span><span class="p">](</span><span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">www</span><span class="o">.</span><span class="n">rfk</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">au</span><span class="o">/</span><span class="n">blog</span><span class="o">/</span><span class="p">)</span> <span class="n">put</span> <span class="n">together</span> <span class="n">a</span> <span class="n">library</span> <span class="n">that</span>
|
|
<span class="n">makes</span> <span class="n">Hawk</span> <span class="n">work</span> <span class="k">as</span> <span class="n">an</span> <span class="n">Authentication</span> <span class="n">provider</span> <span class="k">for</span> <span class="n">them</span><span class="o">.</span> <span class="n">I</span><span class="s1">'m chocked how</span>
|
|
<span class="n">simple</span> <span class="n">it</span> <span class="ow">is</span> <span class="n">to</span> <span class="n">use</span> <span class="n">it</span><span class="o">.</span>
|
|
|
|
<span class="n">Here</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">demo</span> <span class="n">of</span> <span class="n">how</span> <span class="n">we</span> <span class="n">implemented</span> <span class="n">it</span> <span class="k">for</span> <span class="n">Daybed</span><span class="p">:</span>
|
|
|
|
<span class="sb">``</span><span class="err">`</span> <span class="n">sourceCode</span> <span class="n">python</span>
|
|
<span class="kn">from</span> <span class="nn">pyramid_hawkauth</span> <span class="kn">import</span> <span class="n">HawkAuthenticationPolicy</span>
|
|
|
|
<span class="n">policy</span> <span class="o">=</span> <span class="n">HawkAuthenticationPolicy</span><span class="p">(</span><span class="n">decode_hawk_id</span><span class="o">=</span><span class="n">get_hawk_id</span><span class="p">)</span>
|
|
<span class="n">config</span><span class="o">.</span><span class="n">set_authentication_policy</span><span class="p">(</span><span class="n">authn_policy</span><span class="p">)</span>
|
|
</pre></div>
|
|
|
|
|
|
<p>The get_hawk_id function is a function that takes a request and a
|
|
tokenid and returns a tuple of (token_id, token_key).</p>
|
|
<p>How you want to store the tokens and retrieve them is up to you. The
|
|
default implementation (e.g. if you don't pass a decode_hawk_id
|
|
function) decodes the key from the token itself, using a master secret
|
|
on the server (so you don't need to store anything).</p>
|
|
<h2 id="integrate-with-nodejs-express-apps">Integrate with node.js Express apps</h2>
|
|
<p>We had to implement Hawk authentication for two node.js projects and
|
|
finally came up factorizing everything in a library for express, named
|
|
<a href="https://github.com/mozilla-services/express-hawkauth">express-hawkauth</a>.</p>
|
|
<p>In order to plug it in your application, you'll need to use it as a
|
|
middleware:</p>
|
|
<p>``` sourceCode javascript
|
|
var express = require("express");
|
|
var hawk = require("express-hawkauth");
|
|
app = express();</p>
|
|
<p>var hawkMiddleware = hawk.getMiddleware({
|
|
hawkOptions: {},
|
|
getSession: function(tokenId, cb) {
|
|
// A function which pass to the cb the key and algorithm for the
|
|
// given token id. First argument of the callback is a potential
|
|
// error.
|
|
cb(null, {key: "key", algorithm: "sha256"});
|
|
},
|
|
createSession: function(id, key, cb) {
|
|
// A function which stores a session for the given id and key.
|
|
// Argument returned is a potential error.
|
|
cb(null);
|
|
},
|
|
setUser: function(req, res, tokenId, cb) {
|
|
// A function that uses req and res, the hawkId when they're known so
|
|
// that it can tweak it. For instance, you can store the tokenId as the
|
|
// user.
|
|
req.user = tokenId;
|
|
}
|
|
});</p>
|
|
<p>app.get("/hawk-enabled-endpoint", hawkMiddleware);
|
|
```</p>
|
|
<p>If you pass the createSession parameter, all non-authenticated requests
|
|
will create a new hawk session and return it with the response, in the
|
|
Hawk-Session-Token header.</p>
|
|
<p>If you want to only check a valid hawk session exists (without creating
|
|
a new one), just create a middleware which doesn't have any
|
|
createSession parameter defined.</p>
|
|
<h2 id="some-reference-implementations">Some reference implementations</h2>
|
|
<p>As a reference, here is how we're using the libraries I'm talking about,
|
|
in case that helps you to integrate with your projects.</p>
|
|
<ul>
|
|
<li>The Mozilla Loop server <a href="https://github.com/mozilla-services/loop-server/blob/master/loop/index.js#L70-L133">uses hawk as authentication once you're
|
|
logged in with a valid BrowserID
|
|
assertion</a>;
|
|
request, to keep a session between client and server;</li>
|
|
<li><a href="https://github.com/spiral-project/daybed/commit/f178b4e43015fa077430798dcd3d0886c7611caf">I recently added hawk support on the Daybed
|
|
project</a>
|
|
(that's a pyramid / cornice) app.</li>
|
|
<li>It's also interesting to note that Kumar put together <a href="http://hawkrest.readthedocs.org/en/latest/">hawkrest, for
|
|
the django rest
|
|
framework</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<label for="sidebar-checkbox" class="sidebar-toggle"></label>
|
|
|
|
<script>
|
|
(function(document) {
|
|
var i = 0;
|
|
// snip empty header rows since markdown can't
|
|
var rows = document.querySelectorAll('tr');
|
|
for(i=0; i<rows.length; i++) {
|
|
var ths = rows[i].querySelectorAll('th');
|
|
var rowlen = rows[i].children.length;
|
|
if (ths.length > 0 && ths.length === rowlen) {
|
|
rows[i].remove();
|
|
}
|
|
}
|
|
})(document);
|
|
</script>
|
|
|
|
<script>
|
|
/* Lanyon & Poole are Copyright (c) 2014 Mark Otto. Adapted to Pelican 20141223 and extended a bit by @thomaswilley */
|
|
(function(document) {
|
|
var toggle = document.querySelector('.sidebar-toggle');
|
|
var sidebar = document.querySelector('#sidebar');
|
|
var checkbox = document.querySelector('#sidebar-checkbox');
|
|
document.addEventListener('click', function(e) {
|
|
var target = e.target;
|
|
if(!checkbox.checked ||
|
|
sidebar.contains(target) ||
|
|
(target === checkbox || target === toggle)) return;
|
|
checkbox.checked = false;
|
|
}, false);
|
|
})(document);
|
|
</script>
|
|
<!-- Piwik -->
|
|
<script type="text/javascript">
|
|
var _paq = _paq || [];
|
|
_paq.push(['trackPageView']);
|
|
_paq.push(['enableLinkTracking']);
|
|
(function() {
|
|
var u="//tracker.notmyidea.org/";
|
|
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
|
_paq.push(['setSiteId', 3]);
|
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
|
})();
|
|
</script>
|
|
<noscript><p><img src="//tracker.notmyidea.org/piwik.php?idsite=3" style="border:0;" alt="" /></p></noscript>
|
|
<!-- End Piwik Code -->
|
|
</div>
|
|
</body>
|
|
</html> |