Flask-IndieAuth - A Python Library for Micropub Servers

One of the things I like about the IndieWeb community is that while they are building tools for themselves, they also tend to release useful parts under Free Software licenses. This helps other developers join the community more quickly, but it also tends to help improve the quality and feature sets of these projects as others use and add to the source.

One of my favorite things to come from the IndieWeb folks is the Micropub API standard, which defines some simple protocols for clients to send post data (the kinds of things you'd share on a blog or social media: images, short plain text, long articles, tags, and more) to servers for posting. One upshot is that if your server accepts Micropub, you can use one of many clients to put content on your site. I'm using a dedicated editor from Aaron Parecki's Quill to write this post, but there are lots of alternatives that are aimed at special use cases. For example, Kyle Mahan's Woodwind is an IndieWeb reader app that happens to include functionality for posting replies, favorites, reposts, and even RSVPs directly to my site via Micropub.

Another favorite is the idea of IndieAuth for web sign-in. At a high level, the idea is that you create two-way links between your website and your user profile on some other silo. For example, on your homepage you add a link to your Twitter profile and on your Twitter profile you link back to your homepage. For a client that supports IndieAuth, I can log in using my homepage URL by verifying that I can log in to my Twitter account.

My own personal Micropub implementation is a little pile of spaghetti Python code making use of the Flask framework. I use IndieAuth to handle authentication (i.e. - proving that a post comes from an app that I've logged into) and authorization (i.e. - proving that I gave that app permission to post to my site). As I've started improving my Micropub implementation, I found it useful to extract that portion of my code into a library that can be used with other Flask applications.

Introducing Flask-IndieAuth

Flask-IndieAuth is a Flask extension that adds the ability to require a client to send a valid IndieAuth token when making requests to any route. For example:

from flask_indieauth import requires_indieauth

@app.route('/micropub', methods=['GET','POST'])
@requires_indieauth
def handle_micropub():
    # ... handle the request

The @requires_indieauth decorator runs before the code for the route. It currently looks for an IndieAuth token in one of three places, in order:

  • HTTP Header (e.g. "Authorization: Bearer xxxx...xx")
  • HTTP form data or query string (e.g. "?access_token=xxxx...xx")
  • The body of a JSON-encoded POST (e.g. {"access_token": "xxxx...xx"})

If a token is found, it will be verified against the configured Token Endpoint to confirm that it is a valid token issued for your server's configured homepage with a sufficient scope.

For more information on how to install, configure, and use Flask-IndieAuth, please check out the README on GitHub.

Next Steps

I'll be using this extension to build my Micropub media endpoint (coming up in a future post) and so far it is working just fine. That said, I know there is a lot of room for improvement. Some things on my list:

  • "scope" can have many values, but only "post" is supported for now. It should probably be passed as an argument to @requires_indieauth so different routes can have different requirements.
  • The configured homepage ("ME") is currently expected in the Flask app's config. I'm not sure if that's "standard".
  • "TOKEN_ENDPOINT" is currently expected in the Flask app's configuration, but since it is required to be specified in the HTTP headers for or as a <link> in the content for the homepage, this could be fetched by the server.
  • Error handling isn't great - all failure conditions currently return HTTP 400 (Bad Request) but should probably be diversified a bit.

Feedback Welcome!

This is my first published Flask extension (heck, it's my first public Python package on PyPI), and I'd really appreciate comments, questions, pull requests, etc. Feel free to reach out on GitHub, or you can find me in the #indieweb chat on freenode IRC.