So you've got a pleroma instance and some other applications running because you're a nice little sysadmin. Cool. Now you want to provide single-sign-on for these applications that probably don't support it. How do we go about that?

Well, oauth of course. Kinda in the title.

So throughout I am going to assume you're on ubuntu 18.04 or similar.

What we're going for is a library that intercepts requests, checks for an auth token, and if none exists, to bounce to a login page.

This probably also works with mastodon, but I haven't tested it.

Setup

First thing's first, we need some libs and such yep

sudo apt-get install lua5.1 liblua5.1-dev libopenssl-dev luarocks
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs npm

yes lua and node. Terrifying, I know.

Now

sudo luarocks install lua-cjson
sudo luarocks install lua-resty-http
sudo luarocks install luaossl
sudo luarocks install --server=http://luarocks.org/dev ngx-oauth-pleroma-mod

Setting up the bouncer

Now we need to set up our bouncer

git clone https://git.ihatebeinga.live/IHBAGang/nginx-oauth-sidecar.git
cd nginx-oauth-sidecar
npm i

And run it with the following systemd unit file

[Unit]
Description=Oauth bouncer
After=network.target

[Service]
User=myuser
WorkingDirectory=/home/myuser/nginx-oauth-sidecar
Environment="PORT=8090"
Environment="NODE_ENV=production"
ExecStart=/usr/bin/node index.js
ExecReload=/bin/kill $MAINPID
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target

Actually wiring it up

Now we've got the basics set up we need to actually wire up an application to use it.

Install the following nginx snippets to /etc/nginx/snippets/

oauth-settings

set $oauth_redirect_uri '/_oauth/callback';
set $oauth_oaas_uri 'https://myfedi.instance/oauth';
set $oauth_scope 'read';
set $oauth_userinfo_url 'https://myfedi.instance/api/v1/accounts/verify_credentials';

oauth-routes - change the port in login-form to the one you're running the bouncer on

location /_oauth/login {
    content_by_lua_file '/usr/local/share/lua/5.1/ngx-oauth-login.lua';
}

location /_oauth/callback {
    content_by_lua_file '/usr/local/share/lua/5.1/ngx-oauth-redirect-handler.lua';
}

location /_oauth/login-form {
    proxy_pass http://localhost:8123;
}

We now need to create a new oauth application to take our auth.

Use this command:

curl \
    -XPOST \
    https://myfedi.instance/api/v1/apps \
    --data-urlencode "client_name=SOME_NAME_IT_DOESNT_MATTER" \
    --data-urlencode "redirect_uris=https://somewhere.myfedi.instance/_oauth/redirect" \
    --data-urlencode 'scopes=read' \
    --header "Content-Type: application/x-www-form-urlencoded"

Note that the redirect_uris MUST end with _oauth/redirect for this to work. The API will throw you back some json with a client id and secret. You'll need them.

Now you can create a nginx host file like so and it'll just work (tm)

server {
    listen 443 ssl http2;
    ssl_session_timeout 5m;
    ssl_certificate      /etc/ssl/private/my.cert;
    ssl_certificate_key   /etc/ssl/private/my.key;
    access_log /var/log/nginx/somewhere.access.log;
    error_log /var/log/nginx/somewhere.error.log;
    server_name somewhere.myfedi.instance;

    set $oauth_client_id 'my-key';
    set $oauth_client_secret 'my-secret';
    include 'snippets/oauth-routes';
    include 'snippets/oauth-settings';

    location / {
        access_by_lua_file '/usr/local/share/lua/5.1/ngx-oauth-proxy.lua';
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_pass http://localhost:MYPORT;
        client_max_body_size 16m;
    }
}

Now https://somewhere.myfedi.instance will throw you off to oauth before allowing you through. Cool, huh?

Known Issues

At least on my setup, sometimes the oauth proxy will shout about not being able to allocate an address. I haven't been able to track this down yet, but you can work around it by refreshing