{"html_url": "https://github.com/simonw/datasette/issues/1858#issuecomment-1292709818", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1858", "id": 1292709818, "node_id": "IC_kwDOBm6k_c5NDS-6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T22:07:04Z", "updated_at": "2022-10-26T22:07:04Z", "author_association": "OWNER", "body": "New token design:\r\n\r\n```json\r\n{\r\n \"a\": \"actor-id\",\r\n \"t\": \"creation timestamp as integer\",\r\n \"d\": \"intended duration in seconds, or blank if no duration set\"\r\n}\r\n```\r\nThis is in place of the `\"e\": \"expiry timestamp\"` design I've built so far.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423364990, "label": "`max_signed_tokens_ttl` setting for a maximum duration on API tokens"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1858#issuecomment-1292708227", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1858", "id": 1292708227, "node_id": "IC_kwDOBm6k_c5NDSmD", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T22:05:34Z", "updated_at": "2022-10-26T22:05:34Z", "author_association": "OWNER", "body": "I just realized this can't easily affect the `datasette create-token` command because it doesn't currently accept the `--setting` option, so it wouldn't know what `max_signed_tokens_ttl` was.\r\n\r\nMore to the point: even if it did, someone could abuse their knowledge of the secret to create a signed non-expiring token even on servers that didn't want to support those.\r\n\r\nSo I actually need to redesign the token format: it needs to store the timestamp when the token was created and the intended duration, NOT the timestamp that the token expires at.\r\n\r\nOtherwise it's not possible for servers to enforce `max_signed_tokens_ttl` - someone could always create a token with a custom `expires_at` timestamp on it outside of the configured limit.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423364990, "label": "`max_signed_tokens_ttl` setting for a maximum duration on API tokens"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1858#issuecomment-1292687774", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1858", "id": 1292687774, "node_id": "IC_kwDOBm6k_c5NDNme", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T21:44:57Z", "updated_at": "2022-10-26T21:44:57Z", "author_association": "OWNER", "body": "I'm going for consistency with `max_csv_mb` and `max_returned_rows` and `allow_signed_tokens` and `default_cache_ttl`.\r\n\r\nSo `max_signed_tokens_ttl`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423364990, "label": "`max_signed_tokens_ttl` setting for a maximum duration on API tokens"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1860#issuecomment-1292685478", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1860", "id": 1292685478, "node_id": "IC_kwDOBm6k_c5NDNCm", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T21:42:35Z", "updated_at": "2022-10-26T21:42:35Z", "author_association": "OWNER", "body": "That's deployed to https://latest.datasette.io/ now - some examples:\r\n\r\n- https://latest.datasette.io/fixtures?sql=--+one+kind+of+comment%0D%0Aselect+*+from+searchable\r\n- https://latest.datasette.io/fixtures?sql=%2F*+Multi%0D%0A++line+comment+*%2F%0D%0Aselect+*+from+searchable\r\n- https://latest.datasette.io/fixtures?sql=%2F*+Both+kinds+*%2F%0D%0A--+of+comment%0D%0A%2F*+and+more+*%2F%0D%0A--+and+more+and+more%0D%0Aselect+*+from+searchable", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1424378012, "label": "SQL query field can't begin by a comment"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1860#issuecomment-1292679567", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1860", "id": 1292679567, "node_id": "IC_kwDOBm6k_c5NDLmP", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T21:36:25Z", "updated_at": "2022-10-26T21:36:25Z", "author_association": "OWNER", "body": "I'm never 100% sure how to tell if a regular expression includes a nasty denial of service attack - are there any inputs that could cause this new regex to execute in quadratic time or similar?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1424378012, "label": "SQL query field can't begin by a comment"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1860#issuecomment-1292678657", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1860", "id": 1292678657, "node_id": "IC_kwDOBm6k_c5NDLYB", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T21:35:23Z", "updated_at": "2022-10-26T21:35:37Z", "author_association": "OWNER", "body": "Here are the new tests - each of these should now work: https://github.com/simonw/datasette/blob/55a709c480a1e7401b4ff6208f37a2cf7c682183/tests/test_utils.py#L170-L175", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1424378012, "label": "SQL query field can't begin by a comment"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1860#issuecomment-1292674919", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1860", "id": 1292674919, "node_id": "IC_kwDOBm6k_c5NDKdn", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T21:31:22Z", "updated_at": "2022-10-26T21:31:22Z", "author_association": "OWNER", "body": "I'm experimenting with this:\r\n```python\r\n# Allow SQL to start with a /* */ or -- comment\r\ncomment_re = (\r\n # Start of string, then any amount of whitespace\r\n r'^(\\s*' +\r\n # Comment that starts with -- and ends at a newline\r\n r'(?:\\-\\-.*?\\n\\s*)' +\r\n # Comment that starts with /* and ends with */\r\n r'|(?:/\\*[\\s\\S]*?\\*/)' + \r\n # Whitespace\r\n r')*\\s*'\r\n)\r\n\r\nallowed_sql_res = [\r\n re.compile(comment_re + r\"select\\b\"),\r\n re.compile(comment_re + r\"explain\\s+select\\b\"),\r\n re.compile(comment_re + r\"explain\\s+query\\s+plan\\s+select\\b\"),\r\n re.compile(comment_re + r\"with\\b\"),\r\n re.compile(comment_re + r\"explain\\s+with\\b\"),\r\n re.compile(comment_re + r\"explain\\s+query\\s+plan\\s+with\\b\"),\r\n]\r\n```\r\nThis should allow any number of comments of either type as a suffix to the allowed SQL patterns.\r\n\r\nNeeds extensive unit tests!\r\n\r\nI'm not massively worried if it has a flaw in it though, since this is part of Datasette's defense in depth: if a non-SELECT query sneaks through it still shouldn't be able to cause any damage as the database connection is read-only or immutable.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1424378012, "label": "SQL query field can't begin by a comment"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1860#issuecomment-1292659986", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1860", "id": 1292659986, "node_id": "IC_kwDOBm6k_c5NDG0S", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T21:14:26Z", "updated_at": "2022-10-26T21:15:22Z", "author_association": "OWNER", "body": "Yeah we should fix this.\r\n\r\nhttps://www.sqlite.org/lang_comment.html - SQLite also supports `-- style` comments.\r\n\r\nI like how explicit the documentation is here:\r\n\r\n> SQL comments begin with two consecutive \"-\" characters (ASCII 0x2d) and extend up to and including the next newline character (ASCII 0x0a) or until the end of input, whichever comes first.\r\n> \r\n> C-style comments begin with \"/*\" and extend up to and including the next \"*/\" character pair or until the end of input, whichever comes first. C-style comments can span multiple lines. ", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1424378012, "label": "SQL query field can't begin by a comment"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1849#issuecomment-1292654852", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1849", "id": 1292654852, "node_id": "IC_kwDOBm6k_c5NDFkE", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T21:08:44Z", "updated_at": "2022-10-26T21:08:44Z", "author_association": "OWNER", "body": "Generally though we should expect that people might try to use `render_template(...)` without passing a `request`, so Datasette core should be able to handle this.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1420174670, "label": "NoneType' object has no attribute 'actor'"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1849#issuecomment-1292654522", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1849", "id": 1292654522, "node_id": "IC_kwDOBm6k_c5NDFe6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T21:08:20Z", "updated_at": "2022-10-26T21:08:20Z", "author_association": "OWNER", "body": "From the stack trace in Sentry:\r\n\r\n\"image\"\r\n\r\nSo this happened because a custom plugin tried to render `forbidden.html` without passing in the `request`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1420174670, "label": "NoneType' object has no attribute 'actor'"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1849#issuecomment-1292653219", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1849", "id": 1292653219, "node_id": "IC_kwDOBm6k_c5NDFKj", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T21:06:56Z", "updated_at": "2022-10-26T21:06:56Z", "author_association": "OWNER", "body": "This was a hit to an authenticated page where the incoming user WAS logged in but did not have permission to view that specific page.\r\n\r\nCode in question: https://github.com/simonw/datasette/blob/c7dd76c26257ded5bcdfd0570e12412531b8b88f/datasette/app.py#L634-L640", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1420174670, "label": "NoneType' object has no attribute 'actor'"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1851#issuecomment-1292592210", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1851", "id": 1292592210, "node_id": "IC_kwDOBm6k_c5NC2RS", "user": {"value": 25778, "label": "eyeseast"}, "created_at": "2022-10-26T20:03:46Z", "updated_at": "2022-10-26T20:03:46Z", "author_association": "CONTRIBUTOR", "body": "Yeah, every time I see something cool done with triggers, I remember that I need to start using triggers.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421544654, "label": "API to insert a single record into an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1851#issuecomment-1292544296", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1851", "id": 1292544296, "node_id": "IC_kwDOBm6k_c5NCqko", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T19:33:34Z", "updated_at": "2022-10-26T19:33:34Z", "author_association": "OWNER", "body": "That trigger solution is pretty neat!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421544654, "label": "API to insert a single record into an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1851#issuecomment-1292519956", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1851", "id": 1292519956, "node_id": "IC_kwDOBm6k_c5NCkoU", "user": {"value": 15178711, "label": "asg017"}, "created_at": "2022-10-26T19:20:33Z", "updated_at": "2022-10-26T19:20:33Z", "author_association": "CONTRIBUTOR", "body": "> This could use a new plugin hook, too. I don't want to complicate your life too much, but for things like GIS, I'd want a way to turn regular JSON into SpatiaLite geometries or combine X/Y coordinates into point geometries and such. Happy to help however I can.\r\n\r\n @eyeseast Maybe you could do this with triggers? Like you can insert JSON-friendly data into a \"raw\" table, and create a trigger that transforms that inserted data into the proper table\r\n\r\nHere's an example:\r\n\r\n```sql\r\n-- meant to be updated from a Datasette insert\r\ncreate table points_raw(longitude int, latitude int);\r\n\r\n-- the target table with proper spatliate geometries\r\ncreate table points(point geometry);\r\n\r\nCREATE TRIGGER insert_points_raw INSERT ON points_raw \r\n BEGIN\r\n insert into points(point) values (makepoint(new.longitude, new.latitude))\r\n END;\r\n```\r\n\r\nYou could then POST a new row to `points_raw` like this:\r\n```\r\nPOST /db/points_raw\r\nAuthorization: Bearer xxx\r\nContent-Type: application/json\r\n{\r\n \"row\": {\r\n \"longitude\": 27.64356,\r\n \"latitude\": -47.29384\r\n }\r\n}\r\n```\r\n\r\nThen SQLite with run the trigger and insert a new row in `points` with the correct geometry point. Downside is you'd have duplicated data with `points_raw`, but maybe it could be a `TEMP` table (or have a cron that deletes all rows from that table every so often?)", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421544654, "label": "API to insert a single record into an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/pull/499#issuecomment-1292401308", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/499", "id": 1292401308, "node_id": "IC_kwDOCGYnMM5NCHqc", "user": {"value": 7908073, "label": "chapmanjacobd"}, "created_at": "2022-10-26T17:54:26Z", "updated_at": "2022-10-26T17:54:51Z", "author_association": "CONTRIBUTOR", "body": "The problem with how it is currently is that the transformed fts table _will_ return incorrect results (unless the table was only 1 row or something), even if create_triggers was enabled previously. Maybe the simplest solution is to disable fts on a transformed table rather than try to recreate it? Thoughts?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1405196044, "label": "feat: recreate fts triggers after table transform"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1860#issuecomment-1292390996", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1860", "id": 1292390996, "node_id": "IC_kwDOBm6k_c5NCFJU", "user": {"value": 562352, "label": "CharlesNepote"}, "created_at": "2022-10-26T17:43:41Z", "updated_at": "2022-10-26T17:43:41Z", "author_association": "NONE", "body": "I guess the issue is here: https://github.com/simonw/datasette/blob/9676b2deb07cff20247ba91dad3e84a4ab0b00d1/datasette/utils/__init__.py#L209\r\n\r\nHere is a working regexp allowing it:\r\n```diff\r\n- re.compile(r\"^select\\b\"),\r\n+ re.compile(r\"^\\s*(/\\*.+?(?=\\*/)\\*/\\s*)*select\"),\r\n```\r\n`^\\s*`: beginning by 0 or an infinite number of \\s (spaces, tabs, newlines...)\r\n`(/\\*.+?(?=\\*/)\\*/\\s*)*`: 0 or an infinite number of chars beginning by `/*` and ending to the next occurrence of `*/` followed by 0 or an infinite number of \\s\r\n\r\nYou can play with the regexp here: https://regex101.com/r/aESXDL/3\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1424378012, "label": "SQL query field can't begin by a comment"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1291485444", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1291485444, "node_id": "IC_kwDOBm6k_c5M-oEE", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T04:30:34Z", "updated_at": "2022-10-26T04:30:34Z", "author_association": "OWNER", "body": "I'm going to delay working on this until after I have some of the write APIs built to try it against:\r\n- #1851", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1859#issuecomment-1291484749", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1859", "id": 1291484749, "node_id": "IC_kwDOBm6k_c5M-n5N", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T04:29:43Z", "updated_at": "2022-10-26T04:29:43Z", "author_association": "OWNER", "body": "Documentation:\r\n- https://docs.datasette.io/en/1.0-dev/authentication.html#datasette-create-token\r\n- https://docs.datasette.io/en/1.0-dev/cli-reference.html#datasette-create-token", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423369494, "label": "datasette create-token CLI command"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1843#issuecomment-1291467084", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1843", "id": 1291467084, "node_id": "IC_kwDOBm6k_c5M-jlM", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T04:03:49Z", "updated_at": "2022-10-26T04:03:49Z", "author_association": "OWNER", "body": "This time I'm suspicious that there are open SQLite files tucked away in thread locals hidden inside my thread pool executor:\r\n\r\nhttps://github.com/simonw/datasette/blob/c7dd76c26257ded5bcdfd0570e12412531b8b88f/datasette/database.py#L24\r\n\r\nhttps://github.com/simonw/datasette/blob/c7dd76c26257ded5bcdfd0570e12412531b8b88f/datasette/database.py#L204-L214", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1408757705, "label": "Intermittent \"Too many open files\" error running tests"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1843#issuecomment-1291466613", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1843", "id": 1291466613, "node_id": "IC_kwDOBm6k_c5M-jd1", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T04:02:56Z", "updated_at": "2022-10-26T04:02:56Z", "author_association": "OWNER", "body": "Just saw this error again!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1408757705, "label": "Intermittent \"Too many open files\" error running tests"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1859#issuecomment-1291439998", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1859", "id": 1291439998, "node_id": "IC_kwDOBm6k_c5M-c9-", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T03:15:13Z", "updated_at": "2022-10-26T03:15:13Z", "author_association": "OWNER", "body": "Reads from `DATASETTE_SECRET` or accepts `--secret` for the signing secret.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423369494, "label": "datasette create-token CLI command"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1859#issuecomment-1291439875", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1859", "id": 1291439875, "node_id": "IC_kwDOBm6k_c5M-c8D", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T03:14:58Z", "updated_at": "2022-10-26T03:14:58Z", "author_association": "OWNER", "body": "Initial design:\r\n\r\n datasette create-token \r\n\r\nOr:\r\n\r\n datasette create-token --expire-after 10m", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423369494, "label": "datasette create-token CLI command"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1858#issuecomment-1291435464", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1858", "id": 1291435464, "node_id": "IC_kwDOBm6k_c5M-b3I", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T03:07:16Z", "updated_at": "2022-10-26T03:07:16Z", "author_association": "OWNER", "body": "This setting will disable the \"Token never expires\" option:\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423364990, "label": "`max_signed_tokens_ttl` setting for a maximum duration on API tokens"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291406219", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291406219, "node_id": "IC_kwDOBm6k_c5M-UuL", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:19:54Z", "updated_at": "2022-10-26T02:59:52Z", "author_association": "OWNER", "body": "I'm going to split the remaining work into separate issues:\r\n\r\n - [x] #1856\r\n - [ ] #1855\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291431132", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291431132, "node_id": "IC_kwDOBm6k_c5M-azc", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:59:50Z", "updated_at": "2022-10-26T02:59:50Z", "author_association": "OWNER", "body": "Documentation: https://docs.datasette.io/en/1.0-dev/authentication.html#api-tokens", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1850#issuecomment-1291430992", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1850", "id": 1291430992, "node_id": "IC_kwDOBm6k_c5M-axQ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:59:33Z", "updated_at": "2022-10-26T02:59:33Z", "author_association": "OWNER", "body": "I started the documentation for the API tokens mechanism here: https://docs.datasette.io/en/1.0-dev/authentication.html#api-tokens", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421529723, "label": "Write API in Datasette core"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1857#issuecomment-1291418546", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1857", "id": 1291418546, "node_id": "IC_kwDOBm6k_c5M-Xuy", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:38:35Z", "updated_at": "2022-10-26T02:38:35Z", "author_association": "OWNER", "body": "I'm going to set a convention that an actor signed in via a token should set `\"token\": \"something\"` as a key. Then the `/-/create-token` view can reject those actors.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423347412, "label": "Prevent API tokens from using /-/create-token to create more tokens"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1850#issuecomment-1291417755", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1850", "id": 1291417755, "node_id": "IC_kwDOBm6k_c5M-Xib", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:36:52Z", "updated_at": "2022-10-26T02:36:58Z", "author_association": "OWNER", "body": "I'm going to set a convention that `\"token\": \"something\"` in an actor means that they were authenticated by a token.\r\n\r\n`\"token\": \"dstok\"` for example.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421529723, "label": "Write API in Datasette core"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1850#issuecomment-1291417100", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1850", "id": 1291417100, "node_id": "IC_kwDOBm6k_c5M-XYM", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:35:32Z", "updated_at": "2022-10-26T02:35:32Z", "author_association": "OWNER", "body": "It strikes me that users should NOT be able to use a token to create additional tokens.\r\n\r\nThe current design actually does allow that, since the `dstok_` Bearer token can be used to authenticate calls to the `/-/create-token` page.\r\n\r\nSo I think I need a mechanism whereby that page can only allow access to users authenticated by cookie.\r\n\r\nNot obvious how to do that though, since Datasette's authentication actor system is designed to abstract that detail away!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421529723, "label": "Write API in Datasette core"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1856#issuecomment-1291410747", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1856", "id": 1291410747, "node_id": "IC_kwDOBm6k_c5M-V07", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:27:05Z", "updated_at": "2022-10-26T02:27:05Z", "author_association": "OWNER", "body": "Because of that I think this is a better name:\r\n\r\n --setting allow_signed_tokens off", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336122, "label": "allow_signed_tokens setting for disabling API signed token mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1856#issuecomment-1291410331", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1856", "id": 1291410331, "node_id": "IC_kwDOBm6k_c5M-Vub", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:26:19Z", "updated_at": "2022-10-26T02:26:19Z", "author_association": "OWNER", "body": "It's a bit confusing that a setting called `allow_create_tokens` also causes incoming `dstok_` tokens to be ignored.\r\n\r\nIs it confusing enough that I should pick a different name for the setting though?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336122, "label": "allow_signed_tokens setting for disabling API signed token mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1856#issuecomment-1291409312", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1856", "id": 1291409312, "node_id": "IC_kwDOBm6k_c5M-Veg", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:24:49Z", "updated_at": "2022-10-26T02:24:49Z", "author_association": "OWNER", "body": "The effect of this setting will be:\r\n\r\n- `/-/create-tokens` interface is no longer available\r\n- Incoming `dstok_` tokens are no longer respected by the following code:\r\n\r\nhttps://github.com/simonw/datasette/blob/b29e487bc3fde6418bf45bda7cfed2e081ff03fb/datasette/default_permissions.py#L52-L72", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336122, "label": "allow_signed_tokens setting for disabling API signed token mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291397623", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291397623, "node_id": "IC_kwDOBm6k_c5M-Sn3", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:11:40Z", "updated_at": "2022-10-26T02:11:40Z", "author_association": "OWNER", "body": "Built a prototype of the `actor_from_request()` hook for this and now:\r\n\r\n```\r\n% curl http://127.0.0.1:8001/-/actor.json -H 'Authorization: Bearer dstok_eyJhIjoicm9vdCIsImUiOm51bGx9.6O1OxgNTFkAU6uw7xNcmXYX949A'\r\n{\"actor\": {\"id\": \"root\", \"dstok\": true}}\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291392887", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291392887, "node_id": "IC_kwDOBm6k_c5M-Rd3", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T02:04:48Z", "updated_at": "2022-10-26T02:04:48Z", "author_association": "OWNER", "body": "Implemented that `dstok_` prefix and the thing where only the `actor[\"id\"]` is copied to the `\"a\"` field.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291290451", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291290451, "node_id": "IC_kwDOBm6k_c5M94dT", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:49:56Z", "updated_at": "2022-10-26T00:49:56Z", "author_association": "OWNER", "body": "Prefix: `dstok_` - for Datasette signed token.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291289369", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291289369, "node_id": "IC_kwDOBm6k_c5M94MZ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:47:46Z", "updated_at": "2022-10-26T00:47:46Z", "author_association": "OWNER", "body": "The tokens also need something that can be used to differentiate them from alternative token mechanisms that other plugins might provide.\r\n\r\nMaybe a prefix before the signed value.\r\n\r\nPrefixes are also useful for scanning to check they were not accidentally committed to source control.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291272280", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291272280, "node_id": "IC_kwDOBm6k_c5M90BY", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:16:09Z", "updated_at": "2022-10-26T00:46:21Z", "author_association": "OWNER", "body": "Other options:\r\n\r\n- `--setting default_api_tokens off`\r\n- `--setting signed_api_tokens off`\r\n- `--setting allow_create_token off`\r\n\r\nThese feel inconsistent because they don't use the `allow_` prefix - but they're also a bit less ugly to look at.\r\n\r\nI like that last one.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291281243", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291281243, "node_id": "IC_kwDOBm6k_c5M92Nb", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:32:21Z", "updated_at": "2022-10-26T00:32:21Z", "author_association": "OWNER", "body": "Rather than duplicating the entire actor into the \"a\" field, maybe just copy the actor ID?\r\n\r\nWould need to restrict token creation to just actors with an ID set.\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291277913", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291277913, "node_id": "IC_kwDOBm6k_c5M91ZZ", "user": {"value": 4399499, "label": "ocdtrekkie"}, "created_at": "2022-10-26T00:26:11Z", "updated_at": "2022-10-26T00:26:11Z", "author_association": "NONE", "body": "> On that basis, I think the model described above where tokens mainly work to provide an \"act on behalf of this actor\" - but with optional additional constraints - is a good one.\n\nThis is what we do for Sandstorm essentially and I fully agree it's the right way to do API tokens in multiuser systems.\n\nConstraints will definitely be important though. I know I want a token to submit error reports programmatically, but I wouldn't want that token to convey my right to delete tables and records, Little Bobby Tables is out there somewhere, and he's all grown up now.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291274835", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291274835, "node_id": "IC_kwDOBm6k_c5M90pT", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:20:48Z", "updated_at": "2022-10-26T00:22:26Z", "author_association": "OWNER", "body": "Tests failed because I added a view without also adding documentation!\r\n\r\nI forgot that the deploy still goes out for branches other than `main` even if the tests aren't passing:\r\n https://github.com/simonw/datasette/blob/c7dd76c26257ded5bcdfd0570e12412531b8b88f/.github/workflows/deploy-latest.yml#L34-L38", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291273609", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291273609, "node_id": "IC_kwDOBm6k_c5M90WJ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:18:40Z", "updated_at": "2022-10-26T00:18:40Z", "author_association": "OWNER", "body": "Another thought about tokens that can act on behalf of the user.\r\n\r\nImagine a user has permission to access a table. They create a token that can create that table... but then their permission is revoked. It would be bad if they could still use that token they created earlier to access that table!\r\n\r\nOn that basis, I think the model described above where tokens mainly work to provide an \"act on behalf of this actor\" - but with optional additional constraints - is a good one.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291272612", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291272612, "node_id": "IC_kwDOBm6k_c5M90Gk", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:16:53Z", "updated_at": "2022-10-26T00:16:53Z", "author_association": "OWNER", "body": "Next step: make these tokens actually do something.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291272414", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291272414, "node_id": "IC_kwDOBm6k_c5M90De", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:16:28Z", "updated_at": "2022-10-26T00:16:28Z", "author_association": "OWNER", "body": "If I'm going to change the naming conventions for settings I should do it before Datasette 1.0.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291271580", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291271580, "node_id": "IC_kwDOBm6k_c5M9z2c", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:14:49Z", "updated_at": "2022-10-26T00:15:06Z", "author_association": "OWNER", "body": "If I'm going to have a setting to disable this feature I need to decide what it will be called.\r\n\r\nClosest existing setting is this one, since it's for a feature that is turned on by default:\r\n\r\n datasette mydatabase.db --setting allow_download off\r\n\r\nSo maybe this?\r\n\r\n datasette mydatabase.db --setting allow_signed_api_tokens off\r\n\r\nI like `allow_signed_api_tokens` more than `allow_api_tokens` because if you install a plugin such as https://datasette.io/plugins/datasette-auth-tokens then API tokens will work even though you disabled this default signed token feature.\r\n\r\n`allow_signed_api_tokens` does feel a bit clumsy/verbose though.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291270227", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291270227, "node_id": "IC_kwDOBm6k_c5M9zhT", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:12:18Z", "updated_at": "2022-10-26T00:12:18Z", "author_association": "OWNER", "body": "Demo is now live at https://latest-1-0-dev.datasette.io/-/create-token - visit https://latest-1-0-dev.datasette.io/login-as-root first to sign in.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291269607", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291269607, "node_id": "IC_kwDOBm6k_c5M9zXn", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:11:15Z", "updated_at": "2022-10-26T00:11:15Z", "author_association": "OWNER", "body": "If you click \"Create token\" for \"Token never expires\" multiple times you currently get exactly the same token each time, since it's just a signed token containing a copy of your actor dictionary.\r\n\r\nI'm not sure if I like that. I could give each token a random ID (maybe using `secrets.token_hex()`) such that different tokens have different identities, which would be useful for logging and auditing and maybe even revocation at some point in the future.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1852#issuecomment-1291268380", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1852", "id": 1291268380, "node_id": "IC_kwDOBm6k_c5M9zEc", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-10-26T00:09:06Z", "updated_at": "2022-10-26T00:09:06Z", "author_association": "OWNER", "body": "Demo:\r\n\r\n![token-demo](https://user-images.githubusercontent.com/9599/197904595-e5651d6c-bafc-4124-b762-71ad94c06ced.gif)\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1421552095, "label": "Default API token authentication mechanism"}, "performed_via_github_app": null}