|
| 1 | +from datasette import hookimpl |
| 2 | +from datasette.utils.asgi import Response |
| 3 | +import httpx |
| 4 | +import urllib |
| 5 | + |
| 6 | + |
| 7 | +async def indieauth(request, datasette): |
| 8 | + client_id = datasette.absolute_url(request, datasette.urls.instance()) |
| 9 | + redirect_uri = datasette.absolute_url(request, request.path) |
| 10 | + |
| 11 | + if request.args.get("code") and request.args.get("me"): |
| 12 | + ok, extra = await verify_code(request.args["code"], client_id, redirect_uri) |
| 13 | + if ok: |
| 14 | + response = Response.redirect(datasette.urls.instance()) |
| 15 | + response.set_cookie( |
| 16 | + "ds_actor", |
| 17 | + datasette.sign( |
| 18 | + { |
| 19 | + "a": { |
| 20 | + "me": extra, |
| 21 | + "display": extra, |
| 22 | + } |
| 23 | + }, |
| 24 | + "actor", |
| 25 | + ), |
| 26 | + ) |
| 27 | + return response |
| 28 | + else: |
| 29 | + return Response.text(extra, status=403) |
| 30 | + |
| 31 | + return Response.html( |
| 32 | + await datasette.render_template( |
| 33 | + "indieauth.html", |
| 34 | + { |
| 35 | + "client_id": client_id, |
| 36 | + "redirect_uri": redirect_uri, |
| 37 | + }, |
| 38 | + request=request, |
| 39 | + ) |
| 40 | + ) |
| 41 | + |
| 42 | + |
| 43 | +async def verify_code(code, client_id, redirect_uri): |
| 44 | + async with httpx.AsyncClient() as client: |
| 45 | + response = await client.post( |
| 46 | + "https://indieauth.com/auth", |
| 47 | + data={ |
| 48 | + "code": code, |
| 49 | + "client_id": client_id, |
| 50 | + "redirect_uri": redirect_uri, |
| 51 | + }, |
| 52 | + ) |
| 53 | + if response.status_code == 200: |
| 54 | + # me=https%3A%2F%2Fsimonwillison.net%2F&scope |
| 55 | + bits = dict(urllib.parse.parse_qsl(response.text)) |
| 56 | + if "me" in bits: |
| 57 | + return True, bits["me"] |
| 58 | + else: |
| 59 | + return False, "Server did not return me=" |
| 60 | + else: |
| 61 | + return False, "{}: {}".format(response.status_code, response.text) |
| 62 | + |
| 63 | + |
| 64 | +@hookimpl |
| 65 | +def register_routes(): |
| 66 | + return [ |
| 67 | + (r"^/-/indieauth$", indieauth), |
| 68 | + ] |
| 69 | + |
| 70 | + |
| 71 | +@hookimpl |
| 72 | +def menu_links(datasette, actor): |
| 73 | + if not actor: |
| 74 | + return [ |
| 75 | + { |
| 76 | + "href": datasette.urls.path("/-/indieauth"), |
| 77 | + "label": "Sign in IndieAuth", |
| 78 | + }, |
| 79 | + ] |
0 commit comments