Creating a Personal IndieAuth Server

I've completed my implementation of an IndieAuth v1.1 server, which has been a goal of mine for some time.

IndieAuth is a great standard for being "OAuth for the Open Web", and within the IndieWeb community, it's seeing some great investment and diversity in use cases. When I was first looking at getting involved in the IndieWeb, I thought that this would be my first project, but instead I ended up going the route of creating a Micropub server and sending Webmentions.

However, over time, I've found that I am starting to outgrow IndieAuth.com as my hosted server, as there are a few extensions I want to provide that only make sense for me, and it gives me yet another project to spend my time building instead of doing the important things in life!

As part of creating my server, I wanted to design it with extensibility in mind, i.e. to allow for refresh_token grants, or to allow for different means of authentication.

I also wanted to make it compliant with the latest version of the IndieAuth specification, as well as take advantage of a few draft RFCs that have been floating around, as an opportunity to see how they feel to work with.

Authentication via Push Notification

As mentioned in Setting up Passwordless Authentication using the Okta Factors API, one of the key reasons of building my own IndieAuth server is to tailor my authentication flow to me. This is primarily to make testing with my staging Micropub server easier, but is also so I can authenticate on a device without needing access to i.e. silo accounts for social login, or to go through an email OTP, which is slower.

I've followed my article and now get a push notification and can allow/deny it, which simplifies things massively! I don't yet have an alternative for when I'm not on my device, however, so there is a single point of failure here, but so far it's made a huge difference.

Consent Screen

Something I took a bit of time to think about (and have follow-up work on) is making the consent screen provide me all the information required to provide informed consent.

I've followed examples from Aaron and Martijn as documented on the IndieWeb Wiki and have ended up with the following initial version:

A screenshot of Jamie's consent screen, listing the client's URL, a text box allowing Jamie to specify the profile URL for the user, a bold warning showing that Proof of Key Code Exchange isn't in use, and a list of scopes and what they mean, presented as a tickbox. There is also a button to authenticate via push notification, or to deny the request

I've shamelessly adapted Aaron's PKCE-specific warning, but have reversed it, warning the user that they're using a less secure version of the request. In the future, I plan to require PKCE for all clients but with the ability to have an allowlist of clients that haven't yet updated.

Pushed Authorization Requests (PAR)

The first thing is that I've experimented with OAuth 2.0 Pushed Authorization Requests which is a proposed IndieAuth extension.

I wanted to play with the ability to pre-authorize requests, for instance rejecting it when there are missing parameters, as well as allowing me to remove the ability to change the URL on the consent page to tweak what the request is for.

But as current clients do not (yet) support this still-draft RFC and proposed IndieAuth extension, I had to rethink this. I implemented this by taking requests to my authorization endpoint, and pre-authorizing them based on the response_type, and if it succeeded, I would create a pushed request, and redirect to my consent page. For instance:

curl -i 'https://indieauth.jvt.me/authorize?redirect_uri=http://localhost%3A8080
  &client_id=https%3A%2F%2Fwww-editor.jvt.me
  &state=state
  &scope=create+update+delete+undelete+media
  &response_type=code
  &me=https://www.jvt.me'

HTTP/1.1 302 Found
Date: Wed, 09 Dec 2020 09:49:12 GMT
Location: https://indieauth.jvt.me/authorize/consent?client_id=https%3A%2F%2Fwww-editor.jvt.me
  &request_uri=urn:ietf:params:oauth:request_uri:MAuFHnwKR9

JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens

For my access tokens, I wanted them to be JSON Web Tokens (JWTs), as they're a format I work with quite a lot, and know that I've got a good choice of libraries, and practice implementing with.

Additionally, I wanted these to be asymmetrically signed, as then it would allow services consuming my IndieAuth server to perform validation of the signature based on a public key, rather than requiring introspection of the access token each time, which simplifies and speeds up the interactions with resource servers (although does have trade-offs once my server can support revocation).

I've followed draft-ietf-oauth-access-token-jwt-10 for this, as a way to make sure that I'm aligning with some of the best practices folks are using, and that hopefully will be turning into a standard RFC before long.

An example of the structure of the token:

{
  "typ": "at+jwt",
  "alg": "RS256"
}
{
  "aud": "https://www-api.jvt.me/",
  "sub": "https://www.jvt.me",
  "auth_time": 1607375072,
  "scope": "update",
  "iss": "https://indieauth.jvt.me",
  "exp": 1609967072,
  "iat": 1607375072,
  "client_id": "https://www-api.jvt.me/post-deploy"
}

These also include an expiry on tokens, to enforce regular re-authentication. In the future, I will look at providing refresh_tokens to allow for longer-lived tokens to apps that require it (such as applications I've written, or other apps that may be adding support in the future for refresh_tokens).

For this initial implementation, I've not set up JWKS-based validation, but there is the ability after this initial release.

RFC7662 Token Introspection Endpoint

As mentioned in this GitHub issue on the IndieAuth spec, I want to try and align my token endpoint with existing OAuth2 requirements, to simplify integrations with OAuth2 libraries.

That being said, as Martijn mentions on GitHub, this wouldn't be able to work as-is anyway, as IndieAuth does not provide a token introspection endpoint aligned with RFC7662: OAuth 2.0 Token Introspection so adding the "active": false is unnecessary.

As part of implementing my IndieAuth server, I've implemented RFC7662 to allow me to modify my resource servers to use the introspect endpoint, which means I can use out-of-the-box OAuth2 libraries.

I've also added "active: true / "active": false to the response of the IndieAuth token endpoint, too, to align with OAuth2.

Code

If you're interested in looking at the code, you can check out the GitLab merge request which introduced it or the source in the repo.

(Note that at the time of writing, there are a couple of outstanding items, but once that's done, it'll be merged and ready!)

Future Improvements

I've already got quite a few things planned for this server, and have raised issues on my repo to track them. Is there anything you recommend me looking into? Get in touch or raise one yourself!

Feedback to give

Something we've spoken about before is that it'd be great to have a test suite for IndieAuth on indieauth.rocks similar to micropub.rocks as a way to validate that the implementation is correct - there are several edge cases, and a lot of requirements, and it'd certainly help with new features as well as preventing regressions.

I will say I'm glad that I've started implementing it after the recent changes to the spec, especially as I'd been around for some of the conversations so was more comfortable with what was coming up.

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#www.jvt.me #indieauth.jvt.me #indieauth.

Also on: IndieWebCamp logo IndieWeb.xyz logo Lobste.rs logo

This post was filed under articles.

Related Posts

Other posts you may be interested in:

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.