Package: datasette-auth-tokens
datasette-auth-tokensDatasette plugin for authenticating access using API tokensInstallationInstall this plugin in the same environment as Datasette.$ pip install datasette-auth-tokensHard-coded tokensRead about Datasette'sauthentication and permissions system.This plugin lets you configure secret API tokens which can be used to make authenticated requests to Datasette.First, create a random API token. A useful recipe for doing that is the following:$ python -c 'import secrets; print(secrets.token_hex(32))'
5f9a486dd807de632200b17508c75002bb66ca6fde1993db1de6cbd446362589Decide on the actor that this token should represent, for example:{"bot_id":"my-bot"}You can then use"allow"blocks to provide that token with permission to access specific actions. To enable access to a configured writable SQL query you could use this in yourmetadata.json:{"plugins":{"datasette-auth-tokens":{"tokens":[{"token":{"$env":"BOT_TOKEN"},"actor":{"bot_id":"my-bot"}}]}},"databases":{":memory:":{"queries":{"show_version":{"sql":"select sqlite_version()","allow":{"bot_id":"my-bot"}}}}}}This uses Datasette'ssecret configuration values mechanismto allow the secret token to be passed as an environment variable.Run Datasette like this:BOT_TOKEN="this-is-the-secret-token" \
    datasette -m metadata.jsonYou can now run authenticated API queries like this:$ curl -H 'Authorization: Bearer this-is-the-secret-token' \
  'http://127.0.0.1:8001/:memory:/show_version.json?_shape=array'
[{"sqlite_version()": "3.31.1"}]Additionally you can allow passing the token as a query string parameter, although that's disabled by default given the security implications of URLs with secret tokens included. This may be useful to easily allow embedding data between different services.Simply enable it using theparamconfig value:{"plugins":{"datasette-auth-tokens":{"tokens":[{"token":{"$env":"BOT_TOKEN"},"actor":{"bot_id":"my-bot"},}],"param":"_auth_token"}},"databases":{":memory:":{"queries":{"show_version":{"sql":"select sqlite_version()","allow":{"bot_id":"my-bot"}}}}}}You can now run authenticated API queries like this:$ curl http://127.0.0.1:8001/:memory:/show_version.json?_shape=array&_auth_token=this-is-the-secret-token
[{"sqlite_version()": "3.31.1"}]Tokens from your databaseAs an alternative (or in addition) to the hard-coded list of tokens you can store tokens in a database table and configure the plugin to access them using a SQL query.Your query needs to take a:token_idparameter and return at least two columns: one calledtoken_secretand one calledactor_*- usuallyactor_id. Furtheractor_prefixed columns can be returned to provide more details for the authenticated actor.Here's a simple example of a configuration query:selectactor_id,actor_name,token_secretfromtokenswheretoken_id=:token_idThis can run against a table like this one:token_idtoken_secretactor_idactor_name1bd3c94f51fcd78Cleopaws286681b4d6f6632PancakesThe tokens are formed as the token ID, then a hyphen, then the token secret. For example:1-bd3c94f51fcd2-86681b4d6f66The SQL query will be executed with the portion before the hyphen as the:token_idparameter.Thetoken_secretvalue returned by the query will be compared to the portion of the token after the hyphen to check if the token is valid.Columns with a prefix ofactor_will be used to populate the actor dictionary. In the above example, a token of2-86681b4d6f66will become an actor dictionary of{"id": 32, "name": "Pancakes"}.To configure this, use a"query"block in your plugin configuration like this:{"plugins":{"datasette-auth-tokens":{"query":{"sql":"select actor_id, actor_name, token_secret from tokens where token_id = :token_id","database":"tokens"}}},"databases":{"tokens":{"allow":{}}}}The"sql"key here contains the SQL query. The"database"key has the name of the attached database file that the query should be executed against - in this case it would execute againsttokens.db.Securing your tokensAnyone with access to your Datasette instance can use it to read thetoken_secretcolumn in your tokens table. This probably isn't what you want!To avoid this, you should lock down access to that table. The configuration example above shows how to do this using an"allow": {}block. Consult Datasette'sPermissions documentationfor more information about how to lock down this kind of access.