Log in
Seblog.nl

#yak

I noticed that in my new login-setup, sessions last much longer. It used to be an hour or 3, but now I have been away from my computer for a full day and I am still logged in.

I like it, because it means that I will see my site more often in a logged-in state (as will others), but I will add to the ever-growing todo-list that I need some 'I trust this computer' checkbox.

Next up must be Webmentions, I feel. I just posted this article with a lot of backlinks to old posts, but since I send all Webmentions in a synchronous way on first-post-visit, it took ages, and I actually got a 502 Bad Gateway out of my site. But I also need weekend, so we will see.

IndieAuth without IndieAuth.com

In February 2017, I added the ability for other people to log in to my site, both via IndieAuth and Twitter. Since June 2019 this feature is more useful, as I moved all my checkins to being ‘private’, meaning there is actually something to see (albeit sparsely posted).

For this, I used the now outdated and deprecated service IndieAuth.com, which back then was also used to log in to the IndieWeb wiki. Due to the confusion between IndieAuth.com (the service) and IndieAuth (the protocol), a new project called IndieLogin was started and the IndieWeb wiki moved to that one. Since it didn’t offer that login service to others, as IndieAuth.com did, I never switched.

Since this week I have a new IndieAuth endpoint for myself. (Meaning: a place I can use to prove my own identity to others.) Unfortunately, it is not compatible with the outdated IndieAuth.com, so I could not use it to prove my identity to my own site. Because this felt wrong (and because I had the IndieAuth flow in my head again now), I decided that my site needed to do that part of IndieAuth on it’s own as well.

Since I was at it, I also have Twitter login working via the new flow. That was easier, because the library support was more plug-and-play.

Juggling with cookies

Last week I wrote about my new setup where I run two applications, the old PHP and the new Elixir, behind an unusual NGINX configuration. For the aforementioned feature of authentication, I had to create even a bigger beast.

All the posts, and all the logic for showing or hiding private posts, are still in PHP. The new authentication is in Elixir. Both ends are storing the information about the current logged-in user in session cookies. How do I merge those two into one user experience?

Luckily, since both applications run under the same domain, the browser is actually unaware of the fact that there are two apps. It just sends all cookies back.

So, in PHP I ask for all headers, look for a cookie-one that is named _yak_key, and if it is there, it makes a direct localhost call to Yak, the Elixir server, with that cookie. Yak exposes one route that just returns the currently logged-in user as a JSON. If that call returns a user, PHP can set a cookie of its own.

Then for the logout, PHP used to redirect to the homepage, but now it redirects to the page that logs you out of Yak, which redirects to the homepage.

At some point this setup will change again, but for now it seems to me as a good way to make one further step in the features I want to support in the new version of my site, while keeping the old one running.


Oh yeah, you may hunt down that localhost-call that checks the login status. It is actually exposed to the outside world too. But if you can provide it with a cookie for a person that is not you, that person and I have a problem anyway, regardless of you knowing the endpoint.

Showing incorrect IndieAuth redirect_uri to the user

Last Thursday I started using my new IndieAuth endpoint, which I can use authorize apps build by others (like the Quill Micropub client), to do stuff with my site (like posting this blogpost for example). In the following weekend I added a lot more validations than just my password, making it a safer endpoint.

One of these validations is the redirect_uri. My previous endpoint already showed this to me on the login-page, so I could manually inspect it, which is a good practice. The spec, however, describes that one should fetch the client_id (which is a URL) and look for a link with the rel-value of `rel="redirect_uri", which can be either in the HTML or in the HTTP Header.

So this is what it (currently) normally looks like:

Image showing the redirect_uri in grey.

And this is what it looks like when the redirect_uri differs from scheme, domain and/or port, and is also not present in at the client_id.

Image showing the redirect_uri in red and with explanation, plus the discovered redirect_uri.

Note that it is okay for Quill not to advertise another redirect_uri, for it is redirecting to a URL with the same scheme, domain and port. It only needs to add the link if it wants a URL where one of these are different. It is now clearer that someone who is not Quill is trying to steal a token.

Yak, strangling PHP with Phoenix and NGINX

At the time of writing this blogpost, my site is written in PHP, and most of the code is many years old. One could call it a legacy application: I was not very good at organising code back when I started it, so it is a bit of a mess.

Like any legacy application, rewriting it would be costly. There are many features I don’t want to lose, but at the same time I don’t want to lock myself up and rewrite them all before I can show and use any work. This kind of ‘Big Bang release’ would eat up all my free time for the next months and my motivation probably won’t make it all the way through.

A Yak to shave

Yesterday I attended the first IndieWebCamp since ‘March 2020’ and I didn’t want to come empty handed. I forced myself to start a new project called Yak.

This is actually the fifth project called ‘Yak’ on my system, but it’s the first one I actually deployed. All the Yak-projects focus on a different part of the app, since there is always something you think you should do first before the rest can start.

The newest and deployed Yak consists of a new IndieAuth endpoint. I was able to shave off this part since IndieAuth tries to be decoupled from the rest of the site: its endpoint might even be on a different domain.

I didn’t go that route though: I decided to strangle.

The simple strangle

With ‘strangle’ I refer to the ‘Strangler Pattern’, after the now more friendly(?) Named term ’Strangler Fig Application’ by Martin Fowler. This plant grows around another tree, slowly taking over its shape and killing it in the process, so that eventually, all that remains is the fig.

In terms of Seblog.nl, I would like Yak to take over more and more features from the PHP-version, until finally Yak is my new CMS. I say ‘PHP-version’, be because yes, Yak is written in Elixir now. How would I go about taking over features then? My first attempt was this:

index index.html index.php;

location /auth {
    # ... proxy headers
    proxy_pass http://localhost:4001;
}

location / {
    try_files $uri $uri/ index.php$is_args$query_string;
}

location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    fastcgi_index /var/www/seblog.nl/index.php;
    fastcgi_param SCRIPT_FILENAME $request_filename;
    fastcgi_param PATH_INFO $fastcgi_script_name;
    include fastcgi_params;
}

That’s a pretty normal PHP setup, but with one path as a proxy. Yak is listening to port 4001 and NGINX sends all traffic for /auth and sub-folders to it. (A firewall keeps visitors for 4001 from outside away, don’t worry.) This way I can have my IndieAuth endpoint be in a different application while still maintaining one domain to the outside.

But since this is my hobby project, I can get niftier.

Proxy all the things

The IndieAuth route is fairly well scoped and easily separated from the rest of the app. For creating posts via Micropub, I can also separate a route and then store the created posts in the same file folder (or database) since both apps run on the same machine. But not all features are that easily separated.

To make it myself easier to add new routes to Yak, I created the following monster:

index index.html;

location @yak {
    # ... proxy headers
    proxy_pass http://localhost:4001;
    proxy_intercept_errors on;
    error_page 404 = /index.php$is_args$query_string;
    error_page 502 = /index.php$is_args$query_string;
}

location / {
    try_files $uri @yak;
}

location ~ \.php$ {
    # same as previous PHP
}

Since Yak is pretty fast (the Phoenix framework logs its response times in microseconds instead of milliseconds), I decided to just route all traffic through Yak. If Yak has the answer, that answer will be the final answer. But all routes that get a 404 Not Found error from Yak, are given to PHP afterwards.

This way the old site still functions: PHP is not even aware that someone has looked at the request before it. I also added the 502 Bad Gateway just in case Yak is down, which I expect only to happen for brief moments during big configuration changing deployments.

A few details to note: it all starts by adding @yak as a final location to try_files. Only the last location can be a virtual one, otherwise I would have put PHP directly in that list. I did remove the $uri/ and removed index.php from the index directive at the top: without this the homepage was not delivered to Yak.

The other crucial bit is the proxy_intercept_errors on, because that traps the errors in the proxy and enables you to add cases for certain errors. With error_page 404 = /index.php$is_args$query_string I send them to PHP. Other errors from Yak, like 401 and 403, are still just rendered as they came from Yak.

Fixing my POST to Micropub

While the above does work for most of my site, it broke my Webmentions and Micropub. (Yes, I tried posting this a few hours ago.) The previously described method makes all requests in PHP come in as GET, which makes sense for rendering an error page.

After a lot of trial and error, reading NGINX documentation and even trying to get Yak to proxy, I decided to localise the few POST requests I needed to be PHP and added those explicit in NGINX.

location /webmention {
    try_files $uri $uri/ /index.php$is_args$args;
}
location /micropub {
    try_files $uri $uri/ /index.php$is_args$args;
}

Once I moved these endpoints to Yak, I can remove them from my NGINX.