html_url,issue_url,id,node_id,user,created_at,updated_at,author_association,body,reactions,issue,performed_via_github_app
https://github.com/simonw/datasette/issues/699#issuecomment-626945281,https://api.github.com/repos/simonw/datasette/issues/699,626945281,MDEyOklzc3VlQ29tbWVudDYyNjk0NTI4MQ==,9599,2020-05-11T20:32:33Z,2020-05-11T20:32:33Z,OWNER,"I did have a bit of trouble with this one-off plugin getting it to load in the correct order - since I need authentication to work if EITHER the one-off plugin spots a token or my `datasette-auth-github` plugin authenticates the user.

That's why I want authentication as a core Datasette concept - so plugins like these can easily play together in a predictable manner.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,
https://github.com/simonw/datasette/issues/699#issuecomment-626943809,https://api.github.com/repos/simonw/datasette/issues/699,626943809,MDEyOklzc3VlQ29tbWVudDYyNjk0MzgwOQ==,9599,2020-05-11T20:30:07Z,2020-05-11T20:31:18Z,OWNER,"I implemented bearer tokens in a private project of mine as a one-off plugin. I'm going to extract that out into a installable plugin soon. For the moment, my `plugins/token_auth.py` file looks like this:
```python
from datasette import hookimpl
import secrets


class TokenAuth:
    def __init__(
        self, app, secret, auth,
    ):
        self.app = app
        self.secret = secret
        self.auth = auth

    async def __call__(self, scope, receive, send):
        if scope.get(""type"") != ""http"":
            return await self.app(scope, receive, send)

        authorization = dict(scope.get(""headers"") or {}).get(b""authorization"") or b""""
        expected = ""Bearer {}"".format(self.secret).encode(""utf8"")

        if secrets.compare_digest(authorization, expected):
            scope = dict(scope, auth=self.auth)

        return await self.app(scope, receive, send)


@hookimpl(trylast=True)
def asgi_wrapper(datasette):
    config = datasette.plugin_config(""token-auth"") or {}
    secret = config.get(""secret"")
    auth = config.get(""auth"")

    def wrap_with_asgi_auth(app):
        return TokenAuth(app, secret=secret, auth=auth,)

    return wrap_with_asgi_auth
```
Then I have the following in `metadata.json`:
```json
{
    ""plugins"": {
        ""token-auth"": {
            ""auth"": {
                ""name"": ""token-bot""
            },
            ""secret"": {
                ""$env"": ""TOKEN_SECRET""
            }
        }
    }
}
```
And a `TOKEN_SECRET` environment variable.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,