<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: authentication</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/authentication.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2021-03-21T05:50:25+00:00</updated><author><name>Simon Willison</name></author><entry><title>Weeknotes: django-sql-dashboard widgets</title><link href="https://simonwillison.net/2021/Mar/21/django-sql-dashboard-widgets/#atom-tag" rel="alternate"/><published>2021-03-21T05:50:25+00:00</published><updated>2021-03-21T05:50:25+00:00</updated><id>https://simonwillison.net/2021/Mar/21/django-sql-dashboard-widgets/#atom-tag</id><summary type="html">
    &lt;p&gt;A few small releases this week, for &lt;code&gt;django-sql-dashboard&lt;/code&gt;, &lt;code&gt;datasette-auth-passwords&lt;/code&gt; and &lt;code&gt;datasette-publish-vercel&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;django-sql-dashboard widgets and permissions&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;, my subset-of-Datasette-for-Django-and-PostgreSQL continues to come together.&lt;/p&gt;
&lt;p&gt;New this week: widgets and permissions.&lt;/p&gt;
&lt;p&gt;To recap: this Django app borrows some ideas from Datasette: it encourages you to create a read-only PostgreSQL user and grant authenticated users the ability to run one or more raw SQL queries directly against your database.&lt;/p&gt;
&lt;p&gt;You can execute more than one SQL query and combine them into a saved dashboard, which will then show multiple tables containing the results.&lt;/p&gt;
&lt;p&gt;This week I added support for dashboard widgets. You can construct SQL queries to return specific column patterns which will then be rendered on the page in different ways.&lt;/p&gt;
&lt;p&gt;There are four widgets at the moment: "big number", bar chart, HTML and Markdown.&lt;/p&gt;
&lt;p&gt;Big number is the simplest: define a SQL query that returns two columns called &lt;code&gt;label&lt;/code&gt; and &lt;code&gt;big_number&lt;/code&gt; and the dashboard will display that result as a big number:&lt;/p&gt;
&lt;div class="highlight highlight-source-sql"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;select&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;Entries&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; label, &lt;span class="pl-c1"&gt;count&lt;/span&gt;(&lt;span class="pl-k"&gt;*&lt;/span&gt;) &lt;span class="pl-k"&gt;as&lt;/span&gt; big_number &lt;span class="pl-k"&gt;from&lt;/span&gt; blog_entry;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt="Entries: 2804 - an example of a big number display" src="https://static.simonwillison.net/static/2021/dashboard-big-number.png" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Bar chart is more sophisticated: return columns named &lt;code&gt;bar_label&lt;/code&gt; and &lt;code&gt;bar_quantity&lt;/code&gt; to display a bar chart of the results:&lt;/p&gt;
&lt;div class="highlight highlight-source-sql"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;select&lt;/span&gt;
  to_char(date_trunc(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;month&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;, created), &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;YYYY-MM&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="pl-k"&gt;as&lt;/span&gt; bar_label,
  &lt;span class="pl-c1"&gt;count&lt;/span&gt;(&lt;span class="pl-k"&gt;*&lt;/span&gt;) &lt;span class="pl-k"&gt;as&lt;/span&gt; bar_quantity
&lt;span class="pl-k"&gt;from&lt;/span&gt;
  blog_entry
&lt;span class="pl-k"&gt;group by&lt;/span&gt;
  bar_label
&lt;span class="pl-k"&gt;order by&lt;/span&gt;
  &lt;span class="pl-c1"&gt;count&lt;/span&gt;(&lt;span class="pl-k"&gt;*&lt;/span&gt;) &lt;span class="pl-k"&gt;desc&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt="A bar chart showing the result of that query" src="https://static.simonwillison.net/static/2021/dashboard-bar-chart.png" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;HTML and Markdown are simpler: they display the rendered HTML or Markdown, after filtering it through the Bleach library to strip any harmful elements or scripts.&lt;/p&gt;
&lt;div class="highlight highlight-source-sql"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;select&lt;/span&gt;
  &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;## Ten most recent blogmarks (of &lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; 
  &lt;span class="pl-k"&gt;||&lt;/span&gt; &lt;span class="pl-c1"&gt;count&lt;/span&gt;(&lt;span class="pl-k"&gt;*&lt;/span&gt;) &lt;span class="pl-k"&gt;||&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt; total)&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-k"&gt;as&lt;/span&gt; markdown &lt;span class="pl-k"&gt;from&lt;/span&gt; blog_blogmark;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I'm running the dashboard application on this blog, and I've set up &lt;a href="https://simonwillison.net/dashboard/example-dashboard/"&gt;an example dashboard&lt;/a&gt; here that illustrates the different types of widget.&lt;/p&gt;
&lt;p&gt;&lt;img alt="An example dashboard with several different widgets" src="https://static.simonwillison.net/static/2021/dashboard-example.png" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Defining custom widgets is easy: take the column names you would like to respond to, sort them alphabetically, join them with hyphens and create a custom widget in a template file with that name.&lt;/p&gt;
&lt;p&gt;So if you wanted to build a widget that looks for &lt;code&gt;label&lt;/code&gt; and &lt;code&gt;geojson&lt;/code&gt; columns and renders that data on a &lt;a href="https://leafletjs.com/"&gt;Leaflet map&lt;/a&gt;, you would create a &lt;code&gt;geojson-label.html&lt;/code&gt; template and drop it into your Django &lt;code&gt;templates/django-sql-dashboard/widgets&lt;/code&gt; folder. See &lt;a href="https://django-sql-dashboard.readthedocs.io/en/latest/widgets.html#custom-widgets"&gt;the custom widgets documentation&lt;/a&gt; for details.&lt;/p&gt;
&lt;p&gt;Which reminds me: I decided a README wasn't quite enough space for documentation here, so I started a &lt;a href="https://django-sql-dashboard.readthedocs.io/"&gt;Read The Docs documentation site&lt;/a&gt; for the project.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.datasette.io/"&gt;Datasette&lt;/a&gt; and &lt;a href="https://sqlite-utils.datasette.io/"&gt;sqlite-utils&lt;/a&gt; both use &lt;a href="https://www.sphinx-doc.org/"&gt;Sphinx&lt;/a&gt; and &lt;a href="https://docutils.sourceforge.io/rst.html"&gt;reStructuredText&lt;/a&gt; for their documentation.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;django-sql-dashboard&lt;/code&gt; I've decided to try out Sphinx and Markdown instead, using &lt;a href="https://myst-parser.readthedocs.io/"&gt;MyST&lt;/a&gt; - a Markdown flavour and parser for Sphinx.&lt;/p&gt;
&lt;p&gt;I picked this because I want to add inline help to &lt;code&gt;django-sql-dashboard&lt;/code&gt;, and since it ships with Markdown as a dependency already (to power the Markdown widget) my hope is that using Markdown for the documentation will allow me to ship some of the user-facing docs as part of the application itself. But it's also a fun excuse to try out MyST, which so far is working exactly as advertised.&lt;/p&gt;
&lt;p&gt;I've seen people in the past avoid Sphinx entirely because they preferred Markdown to reStructuredText, so MyST feels like an important addition to the Python documentation ecosystem.&lt;/p&gt;
&lt;h4&gt;HTTP Basic authentication&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://datasette.io/plugins/datasette-auth-passwords"&gt;datasette-auth-passwords&lt;/a&gt; implements password-based authentication to Datasette. The plugin defaults to providing a username and password login form which sets a signed cookie identifying the current user.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/datasette-auth-passwords/releases/tag/0.4"&gt;Version 0.4&lt;/a&gt; introduces &lt;a href="https://github.com/simonw/datasette-auth-passwords/issues/15"&gt;optional support&lt;/a&gt; for HTTP Basic authentication instead - where the user's browser handles the authentication prompt.&lt;/p&gt;
&lt;p&gt;Basic auth has some disadvantages - most notably that it doesn't support logout without the user entirely closing down their browser. But it's useful for a number of reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It's easy to protect every resource on a website with it - including static assets. Adding &lt;code&gt;"http_basic_auth": true&lt;/code&gt; to your plugin configuration adds this protection, covering all of Datasette's resources.&lt;/li&gt;
&lt;li&gt;It's much easier to authenticate with from automated scripts. &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;roquests&lt;/code&gt; and &lt;code&gt;httpx&lt;/code&gt; all have simple built-in support for passing basic authentication usernames and passwords, which makes it a useful target for scripting - without having to install an additional authentication plugin such as &lt;a href="https://datasette.io/plugins/datasette-auth-tokens"&gt;datasette-auth-tokens&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'm continuing to flesh out authentication options for Datasette, and adding this to &lt;code&gt;datasette-auth-passwords&lt;/code&gt; is one of those small improvements that should pay off long into the future.&lt;/p&gt;
&lt;h4&gt;A fix for datasette-publish-vercel&lt;/h4&gt;
&lt;p&gt;Datasette instances published to &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt; using the &lt;a href="https://datasette.io/plugins/datasette-publish-vercel"&gt;datasette-publish-vercel&lt;/a&gt; have previously been affected by an obscure Vercel bug: &lt;a href="https://github.com/vercel/vercel/issues/5575"&gt;characters such as + in the query string&lt;/a&gt; were being lost due to Vercel unescaping encoded characters before the request got to the Python application server.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://vercel.com/changelog/correcting-request-urls-with-python-serverless-functions"&gt;Vercel fixed this&lt;/a&gt; earlier this month, and the latest release of &lt;code&gt;datasette-publish-vercel&lt;/code&gt; includes their fix by switching to the new &lt;code&gt;@vercel/python&lt;/code&gt; builder. Thanks &lt;a href="https://twitter.com/styfle"&gt;@styfle&lt;/a&gt; from Vercel for shepherding this fix through!&lt;/p&gt;
&lt;h4&gt;New photos on Niche Museums&lt;/h4&gt;
&lt;p&gt;My Niche Museums project has been in hiberation since the start of the pandemic. Now that vaccines are rolling out it feels like there might be an end to this thing, so I've started thinking about my museum hobby again.&lt;/p&gt;
&lt;p&gt;I added some new photos to the site today - on the entries for &lt;a href="https://www.niche-museums.com/17"&gt;Novelty Automation&lt;/a&gt;, &lt;a href="https://www.niche-museums.com/21"&gt;DEVIL-ish Little Things&lt;/a&gt;, &lt;a href="https://www.niche-museums.com/24"&gt;Evergreen Aviation &amp;amp; Space Museum&lt;/a&gt; and &lt;a href="https://www.niche-museums.com/33"&gt;California State Capitol Dioramas&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hopefully someday soon I'll get to visit and add an entirely new museum!&lt;/p&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/django-sql-dashboard/releases/tag/0.4a1"&gt;0.4a1&lt;/a&gt; - (&lt;a href="https://github.com/simonw/django-sql-dashboard/releases"&gt;10 releases total&lt;/a&gt;) - 2021-03-21
&lt;br /&gt;Django app for building dashboards using raw SQL queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-publish-vercel"&gt;datasette-publish-vercel&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-publish-vercel/releases/tag/0.9.2"&gt;0.9.2&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-publish-vercel/releases"&gt;14 releases total&lt;/a&gt;) - 2021-03-20
&lt;br /&gt;Datasette plugin for publishing data using Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-auth-passwords"&gt;datasette-auth-passwords&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-auth-passwords/releases/tag/0.4"&gt;0.4&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-auth-passwords/releases"&gt;9 releases total&lt;/a&gt;) - 2021-03-19
&lt;br /&gt;Datasette plugin for authentication using passwords&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dashboard"&gt;dashboard&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/zeit-now"&gt;zeit-now&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="authentication"/><category term="dashboard"/><category term="django"/><category term="postgresql"/><category term="projects"/><category term="zeit-now"/><category term="weeknotes"/><category term="django-sql-dashboard"/><category term="sphinx-docs"/></entry><entry><title>datasette-auth-passwords</title><link href="https://simonwillison.net/2020/Jul/13/datasette-auth-passwords/#atom-tag" rel="alternate"/><published>2020-07-13T23:39:06+00:00</published><updated>2020-07-13T23:39:06+00:00</updated><id>https://simonwillison.net/2020/Jul/13/datasette-auth-passwords/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-auth-passwords"&gt;datasette-auth-passwords&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
My latest plugin: datasette-auth-passwords provides a mechanism for signing into Datasette using a username and password (which is verified in order to set a ds_actor authentication cookie). So far it only supports passwords that are hard-coded into Datasette’s configuration via environment variables, but I plan to add database-backed user accounts in the future.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/passwords"&gt;passwords&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/plugins"&gt;plugins&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;&lt;/p&gt;



</summary><category term="authentication"/><category term="passwords"/><category term="plugins"/><category term="projects"/><category term="datasette"/></entry><entry><title>Datasette 0.44: The annotated release notes</title><link href="https://simonwillison.net/2020/Jun/12/annotated-release-notes/#atom-tag" rel="alternate"/><published>2020-06-12T03:11:06+00:00</published><updated>2020-06-12T03:11:06+00:00</updated><id>https://simonwillison.net/2020/Jun/12/annotated-release-notes/#atom-tag</id><summary type="html">
    &lt;p&gt;I just released &lt;strong&gt;Datasette 0.44&lt;/strong&gt; to PyPI. With &lt;a href="https://github.com/simonw/datasette/compare/0.43...0.44"&gt;128 commits&lt;/a&gt; since 0.43 this is the biggest release in a long time - and likely the last major release of new features before Datasette 1.0.&lt;/p&gt;

&lt;p&gt;You can read the &lt;a href="https://datasette.readthedocs.io/en/latest/changelog.html#v0-44"&gt;full release notes here&lt;/a&gt;, but I've decided to try something a little different for this release and write up some annotations here on my blog.&lt;/p&gt;

&lt;blockquote&gt;&lt;h4&gt;Writable canned queries&lt;/h4&gt;&lt;p&gt;Datasette's &lt;a href="https://datasette.readthedocs.io/en/latest/sql_queries.html#canned-queries"&gt;Canned queries&lt;/a&gt; feature lets you define SQL queries in &lt;code&gt;metadata.json&lt;/code&gt; which can then be executed by users visiting a specific URL. &lt;a href="https://latest.datasette.io/fixtures/neighborhood_search"&gt;https://latest.datasette.io/fixtures/neighborhood_search&lt;/a&gt; for example.&lt;/p&gt;&lt;p&gt;Canned queries were previously restricted to &lt;code&gt;SELECT&lt;/code&gt;, but Datasette 0.44 introduces the ability for canned queries to execute &lt;code&gt;INSERT&lt;/code&gt; or &lt;code&gt;UPDATE&lt;/code&gt; queries as well, using the new &lt;code&gt;"write": true&lt;/code&gt; property (&lt;a href="https://github.com/simonw/datasette/issues/800"&gt;#800&lt;/a&gt;)&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I originally intended this to be the main feature in the release.&lt;/p&gt;

&lt;p&gt;Datasette 0.37 added the ability for plugins to &lt;a href="https://simonwillison.net/2020/Feb/26/weeknotes-datasette-writes/"&gt;write to the database&lt;/a&gt;. This marked a pretty huge philosophical shift for Datasette: from a read-only publishing system to a framework for building interactive SQL-driven applications. But you needed a plugin, such as my &lt;a href="https://github.com/simonw/datasette-upload-csvs"&gt;datasette-upload-csvs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I realized &lt;a href="https://github.com/simonw/datasette/issues/698"&gt;back in March&lt;/a&gt; that canned queries could provide a simple, sensible way to start adding write functionality to Datasette core. A query could be configured like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "databases": {
    "my-database": {
      "queries": {
        "add_twitter_handle": {
          "sql": "insert into twitter_handles (username) values (:username)",
          "write": true
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And Datasette could then provide a form interface at &lt;code&gt;/my-database/add_twitter_handle&lt;/code&gt; for executing that query. How hard could that be to implement?&lt;/p&gt;

&lt;p&gt;The problem with "simple" features like this is that they open up a cascade of other features.&lt;/p&gt;

&lt;p&gt;If you're going to have write queries, you probably need to restrict who can execute them - which means authentication and permissions. If forms are performing POSTs you need CSRF protection. If users are changing state you need a way to show them messages telling them what happened.&lt;/p&gt;

&lt;p&gt;So let's talk about authentication and permissions.&lt;/p&gt;

&lt;blockquote&gt;&lt;h4&gt;Authentication&lt;/h4&gt;&lt;p&gt;Prior to this release the Datasette ecosystem has treated authentication as exclusively the realm of plugins, most notably through &lt;a href="https://github.com/simonw/datasette-auth-github"&gt;datasette-auth-github&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;0.44 introduces &lt;a href="https://datasette.readthedocs.io/en/latest/authentication.html#authentication"&gt;Authentication and permissions&lt;/a&gt; as core Datasette concepts (&lt;a href="https://github.com/simonw/datasette/issues/699"&gt;#699&lt;/a&gt;). This makes it easier for different plugins can share responsibility for authenticating requests - you might have one plugin that handles user accounts and another one that allows automated access via API keys, for example.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I demonstrated with &lt;a href="https://github.com/simonw/datasette-auth-github"&gt;datasette-auth-github&lt;/a&gt; and &lt;a href="https://github.com/simonw/datasette-auth-existing-cookies"&gt;datasette-auth-existing-cookies&lt;/a&gt; that authentication could exist as completely separate layer from Datasette call (thanks to the magic of ASGI middleware). But I'd started to run into limitations of this approach.&lt;/p&gt;

&lt;p&gt;Crucially, I wanted to be able to support more than one kind of authentication. Users might get authenticated with cookies (via a SSO mechanism such as GitHub's) but API clients need API keys. Now the different authentication plugins need to make sure they don't accidentally intefere with each other's logic.&lt;/p&gt;

&lt;p&gt;Authentication in web applications always comes down to the same thing: inspecting aspects of the incoming HTTP request (headers, cookies, querystring variables etc) and deciding if they prove that the request is coming from a specific user or API integration.&lt;/p&gt;

&lt;p&gt;I decided to use the word "actor" for this, since "user or API integration" is a bit of a mess. This means that authentication can work entirely via a new plugin hook, &lt;a href="https://datasette.readthedocs.io/en/stable/plugins.html#actor-from-request-datasette-request"&gt;actor_from_request&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a really simple implementation of that hook, copied directly from the documentation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from datasette import hookimpl
import secrets

SECRET_KEY = "this-is-a-secret"

@hookimpl
def actor_from_request(datasette, request):
    authorization = request.headers.get("authorization") or ""
    expected = "Bearer {}".format(SECRET_KEY)

    if secrets.compare_digest(authorization, expected):
        return {"id": "bot"}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It returns an "actor dictionary" describing the authenticated actor. Datasette currently has no opinion at all on what shape this dictionary should take, though I expect conventions to emerge over time.&lt;/p&gt;

&lt;p&gt;I have &lt;a href="https://github.com/simonw/datasette/commit/9f236c4c00689a022fd1d508f2b809ee2305927f"&gt;a new policy&lt;/a&gt; of never releasing a new plugin hook without also building a real-world plugin with it. For &lt;code&gt;actor_from_request&lt;/code&gt; I've released &lt;a href="https://github.com/simonw/datasette-auth-tokens"&gt;datasette-auth-tokens&lt;/a&gt;, which lets you create secret API tokens in a configuration file and specify which actions they are allowed to perfom.&lt;/p&gt;

&lt;blockquote&gt;&lt;h4&gt;Permissions&lt;/h4&gt;&lt;p&gt;Datasette also now has a built-in concept of &lt;a href="https://datasette.readthedocs.io/en/stable/authentication.html#authentication-permissions"&gt;Permissions&lt;/a&gt;. The permissions system answers the following question:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Is this &lt;strong&gt;actor&lt;/strong&gt; allowed to perform this &lt;strong&gt;action&lt;/strong&gt;, optionally against this particular &lt;strong&gt;resource&lt;/strong&gt;?&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You can use the new &lt;code&gt;"allow"&lt;/code&gt; block syntax in &lt;code&gt;metadata.json&lt;/code&gt; (or &lt;code&gt;metadata.yaml&lt;/code&gt;) to set required permissions at the instance, database, table or canned query level. For example, to restrict access to the &lt;code&gt;fixtures.db&lt;/code&gt; database to the &lt;code&gt;"root"&lt;/code&gt; user:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;{
    "databases": {
        "fixtures": {
            "allow": {
                "id" "root"
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;See &lt;a href="https://datasette.readthedocs.io/en/stable/authentication.html#authentication-permissions-allow"&gt;Defining permissions with "allow" blocks&lt;/a&gt; for more details.&lt;/p&gt;&lt;p&gt;Plugins can implement their own custom permission checks using the new &lt;a href="https://datasette.readthedocs.io/en/stable/plugins.html#plugin-hook-permission-allowed"&gt;permission_allowed(datasette, actor, action, resource)&lt;/a&gt; hook.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Authentication on its own isn't enough: you also need a way of deciding if an authenticated actor has permission to perform a specific action.&lt;/p&gt;

&lt;p&gt;I was dreading adding permissions to Datasette. I have a long-running feud with Amazon IAM and the Google Cloud equivalent: I've been using AWS for over a decade and I still get completely lost any time I try to figure out the minimum set of permissions for something.&lt;/p&gt;

&lt;p&gt;But... I want Datasette permissions to be flexible. My dream for Datasette is to nurture a growing ecosystem of plugins that can solve data collaboration and analysis problems way beyond what I've imagined myself.&lt;/p&gt;

&lt;p&gt;Thanks to Datasette's plugin hooks, I think I've found a way to provide powerful plugins with minimum additional footprint to Datasette itself.&lt;/p&gt;

&lt;p&gt;The key is the new &lt;a href="https://datasette.readthedocs.io/en/stable/plugins.html#plugin-hook-permission-allowed"&gt;permission_allowed()&lt;/a&gt; hook, which lets plugins receive an actor, action and optional resource and allows them to reply with "allow", "deny" or "I don't know, ask someone else".&lt;/p&gt;

&lt;p&gt;Its partner is the &lt;a href="https://datasette.readthedocs.io/en/stable/internals.html#await-permission-allowed-actor-action-resource-none-default-false"&gt;datasette.permission_allowed(actor, action, resource)&lt;/a&gt; method, which plugins (and Datasette core) can call to check if the current actor is allowed to perform an action against a given resource (a specific database, table or canned query).&lt;/p&gt;

&lt;p&gt;I invented a JSON/YAML based syntax for defining simple permission rules. If you want to provide access to a table to a specific user you can do so like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
    "allow": {
        "id": "simonw"
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or you can grant access to any actor with a role of "staff" like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
    "allow": {
        "role": "staff"
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What are roles here? They're nothing at all to Datasette itself. Datasette authentication plugins can create actors of any shape, so if your plugin decides that "role" is a useful concept it can just bake it into the Datasette.&lt;/p&gt;

&lt;p&gt;You can read more about &lt;a href="https://datasette.readthedocs.io/en/stable/authentication.html#defining-permissions-with-allow-blocks"&gt;how these "allow" blocks work&lt;/a&gt; in the documentation.&lt;/p&gt;

&lt;h4&gt;/-/permissions debug tool&lt;/h4&gt;

&lt;p&gt;Given my ongoing battle with opaque permission systems, I'm determined to try and make Datasette's take on permissions as transparent as possible. The new &lt;code&gt;/-/permissions&lt;/code&gt; page - visible only to authenticated users with the &lt;code&gt;debug-permissions&lt;/code&gt; permission - shows a rolling log of the last 200 permission checks carried out by that instance. My hope is that instance administrators and plugin authors can use this to figure out exactly what is going on.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2020/Debug_permissions.png" alt="/-/permissions screenshot" style="max-width: 100%" /&gt;&lt;/p&gt;

&lt;h4&gt;datasette-permissions-sql&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/simonw/datasette-permissions-sql"&gt;datasette-permissions-sql&lt;/a&gt; is my new proof-of-concept plugin that puts the permission hook to use.&lt;/p&gt;

&lt;p&gt;It exercises a Datasette pattern I find really interesting: using SQL queries to configure custom behaviour. I first started eploring this in &lt;a href="https://github.com/simonw/datasette-atom"&gt;datasette-atom&lt;/a&gt; and &lt;a href="https://github.com/simonw/datasette-ics"&gt;datasette-ics&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is best illustrated by an example &lt;code&gt;metadata.yaml&lt;/code&gt; file. I prefer YAML over JSON for anything that includes a SQL query because YAML has support for multi-line strings:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;databases:
  mydatabase:
    queries:
      promote_to_staff:
        sql: |-
          UPDATE users
          SET is is_staff=1
          WHERE id=:id
        write: true
plugins:
  datasette-permissions-sql:
  - action: view-query
    resource:
    - mydatabase
    - promote_to_staff
    sql: |-
      SELECT * FROM users
      WHERE is_staff = 1
      AND id = :actor_id&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This block does two things. It configures a writable canned query called &lt;code&gt;promote_to_staff&lt;/code&gt;. It then uses &lt;code&gt;datasette-permissions-sql&lt;/code&gt; to define a permission rule that says that only authenticated actors who's &lt;code&gt;id&lt;/code&gt; appears in the &lt;code&gt;users&lt;/code&gt; table with &lt;code&gt;is_staff=1&lt;/code&gt; are allewod to execute that canned query&lt;/p&gt;

&lt;p&gt;This is the beginnings of a full user management system in just a few lines of configuration. I'm really excited about exploring this concept further.&lt;/p&gt;

&lt;blockquote&gt;&lt;h4&gt;register_routes() plugin hooks&lt;/h4&gt;&lt;p&gt;Plugins can now register new views and routes via the &lt;a href="https://datasette.readthedocs.io/en/stable/plugins.html#plugin-register-routes"&gt;register_routes()&lt;/a&gt; plugin hook (&lt;a href="https://github.com/simonw/datasette/issues/819"&gt;#819&lt;/a&gt;). View functions can be defined that accept any of the current &lt;code&gt;datasette&lt;/code&gt; object, the current &lt;code&gt;request&lt;/code&gt;, or the ASGI &lt;code&gt;scope&lt;/code&gt;, &lt;code&gt;send&lt;/code&gt; and &lt;code&gt;receive&lt;/code&gt; objects.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I thought the &lt;a href="https://datasette.readthedocs.io/en/stable/plugins.html#asgi-wrapper-datasette"&gt;asgi_wrapper() hook&lt;/a&gt; might be enough to allow plugins to add their own custom routes and views, but the more I worked with it the more I wanted something a bit more high level.&lt;/p&gt;

&lt;p&gt;Inspired by &lt;a href="https://docs.pytest.org/ "&gt;pytest&lt;/a&gt;, the view functions you can define using &lt;code&gt;register_routes()&lt;/code&gt; benefit from a simple form of dependency injection. Any of the following counts as a valid view function - it will be called with the arguments it requests:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;async def hello(request):
    return Response.html("Hello {}".format(
        html.escape(request.args.get("name"))
    ))

async def page(request, datasette):
    page = await datasette.get_databse().execute(
        "select body from pages where id = :id", {
            "id: request.url_vars["page_id"]
        }
    ).first()
    return Response.html(body)

async def asgi_hello_world(scope, receive, send):
    assert scope["type"] == "http"
    await send(
        {
            "type": "http.response.start",
            "status": 200,
            "headers": [
                [b"content-type", b"application/json"]
            ],
        }
    )
    await send({
        "type": "http.response.body",
        "body": b'{"hello": "world"}'
    })&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here's &lt;a href="https://github.com/simonw/datasette/blob/b906030235efbdff536405d66078f4868ce0d3bd/datasette/utils/__init__.py#L850-L871"&gt;the code that makes this work&lt;/a&gt; - utility functions that pass in just the arguments that match a function's signature.&lt;/p&gt;

&lt;p&gt;Tucked away at the end of the 0.44 release notes is this:&lt;/p&gt;

&lt;blockquote&gt;&lt;h4&gt;The road to Datasette 1.0&lt;/h4&gt;&lt;p&gt;I've assembled a &lt;a href="https://github.com/simonw/datasette/milestone/7"&gt;milestone for Datasette 1.0&lt;/a&gt;. The focus of the 1.0 release will be the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Signify confidence in the quality/stability of Datasette&lt;/li&gt;&lt;li&gt;Give plugin authors confidence that their plugins will work for the whole 1.x release cycle&lt;/li&gt;&lt;li&gt;Provide the same confidence to developers building against Datasette JSON APIs&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you have thoughts about what you would like to see for Datasette 1.0 you can join &lt;a href="https://github.com/simonw/datasette/issues/519"&gt;the conversation on issue #519&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;It's time to start working towards the big 1.0!&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/permissions"&gt;permissions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/plugins"&gt;plugins&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/releasenotes"&gt;releasenotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/annotated-release-notes"&gt;annotated-release-notes&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="authentication"/><category term="permissions"/><category term="plugins"/><category term="projects"/><category term="releasenotes"/><category term="datasette"/><category term="weeknotes"/><category term="annotated-release-notes"/></entry><entry><title>NGINX: Authentication Based on Subrequest Result</title><link href="https://simonwillison.net/2019/Oct/4/nginx-authentication-based-subrequest-result/#atom-tag" rel="alternate"/><published>2019-10-04T15:36:33+00:00</published><updated>2019-10-04T15:36:33+00:00</updated><id>https://simonwillison.net/2019/Oct/4/nginx-authentication-based-subrequest-result/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-subrequest-authentication/"&gt;NGINX: Authentication Based on Subrequest Result&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
TIL about this neat feature of NGINX: you can use the auth_request directive to cause NGINX to make an HTTP subrequest to a separate authentication server for each incoming HTTP request. The authentication server can see the cookies on the incoming request and tell NGINX if it should fulfill the parent request (via a 2xx status code) or if it should be denied (by returning a 401 or 403). This means you can run NGINX as an authenticating proxy in front of any HTTP application and roll your own custom authentication code as a simple webhook-recieving endpoint.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://github.com/simonw/datasette-auth-github/issues/45"&gt;Ishan Anand&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nginx"&gt;nginx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webhooks"&gt;webhooks&lt;/a&gt;&lt;/p&gt;



</summary><category term="authentication"/><category term="nginx"/><category term="webhooks"/></entry><entry><title>django-piston</title><link href="https://simonwillison.net/2009/Apr/30/jespern/#atom-tag" rel="alternate"/><published>2009-04-30T19:55:15+00:00</published><updated>2009-04-30T19:55:15+00:00</updated><id>https://simonwillison.net/2009/Apr/30/jespern/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://bitbucket.org/jespern/django-piston/wiki/Home"&gt;django-piston&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Promising looking Django mini-framework for creating RESTful APIs, from the bitbucket team. Ticks all of Jacob’s boxes, even including built-in pluggable authentication support with HTTP Basic, Digest and OAuth out of the box.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apis"&gt;apis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/bitbucket"&gt;bitbucket&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/digest"&gt;digest&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jespernoehr"&gt;jespernoehr&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/oauth"&gt;oauth&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/piston"&gt;piston&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rest"&gt;rest&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/restful"&gt;restful&lt;/a&gt;&lt;/p&gt;



</summary><category term="apis"/><category term="authentication"/><category term="bitbucket"/><category term="digest"/><category term="django"/><category term="jespernoehr"/><category term="oauth"/><category term="piston"/><category term="python"/><category term="rest"/><category term="restful"/></entry><entry><title>Google's Usability Research on Federated Login</title><link href="https://simonwillison.net/2008/Sep/22/usability/#atom-tag" rel="alternate"/><published>2008-09-22T20:56:33+00:00</published><updated>2008-09-22T20:56:33+00:00</updated><id>https://simonwillison.net/2008/Sep/22/usability/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://sites.google.com/site/oauthgoog/UXFedLogin"&gt;Google&amp;#x27;s Usability Research on Federated Login&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Fascinating—suggests an approach to federated auth based on the Amazon.com “Yes, I have a password” login flow. Feels convoluted to me but apparently it tests really well against a mainstream audience. The more research shared around this stuff the better.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/amazon"&gt;amazon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/federated"&gt;federated&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/login"&gt;login&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openid"&gt;openid&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/usability"&gt;usability&lt;/a&gt;&lt;/p&gt;



</summary><category term="amazon"/><category term="authentication"/><category term="federated"/><category term="google"/><category term="login"/><category term="openid"/><category term="usability"/></entry><entry><title>Quoting Nick Mathewson</title><link href="https://simonwillison.net/2008/May/13/something/#atom-tag" rel="alternate"/><published>2008-05-13T08:06:07+00:00</published><updated>2008-05-13T08:06:07+00:00</updated><id>https://simonwillison.net/2008/May/13/something/#atom-tag</id><summary type="html">
    &lt;blockquote cite="http://www.links.org/?p=326"&gt;&lt;p&gt;Something you had, Something you forgot, Something you were&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="http://www.links.org/?p=326"&gt;Nick Mathewson&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nick-mathewson"&gt;nick-mathewson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;&lt;/p&gt;



</summary><category term="authentication"/><category term="nick-mathewson"/><category term="security"/></entry><entry><title>Django snippets: Authenticate against Active Directory</title><link href="https://simonwillison.net/2007/Dec/10/django/#atom-tag" rel="alternate"/><published>2007-12-10T08:40:09+00:00</published><updated>2007-12-10T08:40:09+00:00</updated><id>https://simonwillison.net/2007/Dec/10/django/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.djangosnippets.org/snippets/501/"&gt;Django snippets: Authenticate against Active Directory&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Uses a custom authentication backend with the Python ldap module. If Django hasn’t seen the user before a new Django user account is created with data from ldap.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/activedirectory"&gt;activedirectory&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ldap"&gt;ldap&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="activedirectory"/><category term="authentication"/><category term="django"/><category term="ldap"/><category term="python"/></entry><entry><title>OAuth Core 1.0</title><link href="https://simonwillison.net/2007/Dec/5/oauth/#atom-tag" rel="alternate"/><published>2007-12-05T03:39:10+00:00</published><updated>2007-12-05T03:39:10+00:00</updated><id>https://simonwillison.net/2007/Dec/5/oauth/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://oauth.net/core/1.0/"&gt;OAuth Core 1.0&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The final spec. Expect to see this crop up all over the place in the next few months.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apis"&gt;apis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/oauth"&gt;oauth&lt;/a&gt;&lt;/p&gt;



</summary><category term="apis"/><category term="authentication"/><category term="oauth"/></entry><entry><title>OAuth: Your valet key for the Web</title><link href="https://simonwillison.net/2007/Sep/21/oauth/#atom-tag" rel="alternate"/><published>2007-09-21T23:34:51+00:00</published><updated>2007-09-21T23:34:51+00:00</updated><id>https://simonwillison.net/2007/Sep/21/oauth/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://journals.aol.com/panzerjohn/abstractioneer/entries/2007/09/21/oauth-your-valet-key-for-the-web/1550"&gt;OAuth: Your valet key for the Web&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
OAuth is a really important new specification that aims to solve the “give this application permission to do X on my behalf” problem once and for all.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apis"&gt;apis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/oauth"&gt;oauth&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openid"&gt;openid&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/specification"&gt;specification&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-services"&gt;web-services&lt;/a&gt;&lt;/p&gt;



</summary><category term="apis"/><category term="authentication"/><category term="oauth"/><category term="openid"/><category term="specification"/><category term="web-services"/></entry><entry><title>Jottit</title><link href="https://simonwillison.net/2007/Sep/16/jottit/#atom-tag" rel="alternate"/><published>2007-09-16T21:43:16+00:00</published><updated>2007-09-16T21:43:16+00:00</updated><id>https://simonwillison.net/2007/Sep/16/jottit/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://jottit.com/"&gt;Jottit&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Aaron Swartz’s latest venture: a complete rethink of the Infogami concept. Well worth checking out for the extremely thoughtful way it introduces features, and the way account creation with a password remains optional until you want to add access control.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/aaron-swartz"&gt;aaron-swartz&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/bitbots"&gt;bitbots&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/infogami"&gt;infogami&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jottit"&gt;jottit&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/usability"&gt;usability&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/userflow"&gt;userflow&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/wiki"&gt;wiki&lt;/a&gt;&lt;/p&gt;



</summary><category term="aaron-swartz"/><category term="authentication"/><category term="bitbots"/><category term="infogami"/><category term="jottit"/><category term="usability"/><category term="userflow"/><category term="wiki"/></entry><entry><title>Wrong-headed impersonation</title><link href="https://simonwillison.net/2007/Mar/5/kim/#atom-tag" rel="alternate"/><published>2007-03-05T14:38:58+00:00</published><updated>2007-03-05T14:38:58+00:00</updated><id>https://simonwillison.net/2007/Mar/5/kim/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.identityblog.com/?p=701"&gt;Wrong-headed impersonation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Kim Cameron discusses user absent authentication, and emphasises the importance of delegation using delegation coupons.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/delegation"&gt;delegation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/delegationcoupons"&gt;delegationcoupons&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/identity"&gt;identity&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/kimcameron"&gt;kimcameron&lt;/a&gt;&lt;/p&gt;



</summary><category term="authentication"/><category term="delegation"/><category term="delegationcoupons"/><category term="identity"/><category term="kimcameron"/></entry><entry><title>How is Google giving me access to this page?</title><link href="https://simonwillison.net/2006/Dec/27/how-is-google-giving-me/#atom-tag" rel="alternate"/><published>2006-12-27T14:38:00+00:00</published><updated>2006-12-27T14:38:00+00:00</updated><id>https://simonwillison.net/2006/Dec/27/how-is-google-giving-me/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="http://ask.metafilter.com/53894/How-is-Google-giving-me-access-to-this-page#811885"&gt;How is Google giving me access to this page?&lt;/a&gt; on Ask MetaFilter&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Google have an open URL redirector, so you can craft a link that uses that:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.google.com/url?sa=D&amp;amp;q=http://www.stratfor.com/products/premium/read_article.php?id=282226"&gt;Link via redirector&lt;/a&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ask-metafilter"&gt;ask-metafilter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web"&gt;web&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="ask-metafilter"/><category term="authentication"/><category term="google"/><category term="security"/><category term="web"/></entry></feed>