Files
docudjeex/content/en/3.serveex/3.security/4.tinyauth.md
T

372 lines
12 KiB
Markdown

---
title: TinyAuth
description: Install TinyAuth, a lightweight forward-auth proxy, and pair it with Pocket ID to add SSO login in front of your self-hosted apps. Protect your app behind Swag with forward-auth.
---
:ellipsis{left=0px width=40rem top=10rem blur=140px zIndex=60}
[TinyAuth](https://tinyauth.app) is a small forward-auth proxy: a single login page that Swag can insert in front of any app before letting a request through, checking whether a visitor is authenticated before forwarding them on.
![tinyauth](/img/serveex/tinyauth.png)
It supports a simple local username/password login out of the box, and can also delegate login to an external OIDC provider. Here we'll use [Pocket ID](/serveex/security/pocket-id), so anyone visiting a protected app first authenticates with a passkey via Pocket ID, then gets forwarded through.
- [TinyAuth documentation](https://tinyauth.app/docs)
- [TinyAuth on GitHub](https://github.com/tinyauthapp/tinyauth)
::note
This guide assumes you've already installed [Pocket ID](/serveex/security/pocket-id). You can skip the Pocket ID sections below and use TinyAuth with just a local username/password instead.
::
## Installation
::file-tree
---
tree:
/:
- docker:
- tinyauth:
- compose.yaml
- .env
- data/
---
::
::steps{level="3"}
### Create the data folder
```bash [Terminal]
sudo mkdir -p /docker/tinyauth/data
```
### Generate a password hash
```bash [Terminal]
sudo docker run -i -t --rm ghcr.io/tinyauthapp/tinyauth:v5 user create --interactive
```
::note
Enable "Format for Docker" when prompted, so the generated hash is already escaped for use in a `.env` file.
::
### Deploy the stack
Open Dockge, click `compose`, name the stack `tinyauth`, and add the following config:
```yaml [compose.yaml]
---
services:
tinyauth:
image: ghcr.io/tinyauthapp/tinyauth:v5
container_name: tinyauth
restart: unless-stopped
env_file:
- .env
volumes:
- /docker/tinyauth/data:/data
ports:
- 3000:3000
```
::tip{icon=""}
✨ Add the Watchtower label to automate updates:
```yaml [compose.yaml]
---
services:
tinyauth:
#...
labels:
- com.centurylinklabs.watchtower.enable=true
```
::
### Set your environment variables
Fill in the `.env` file:
```properties [.env]
TINYAUTH_APPURL=https://tinyauth.mydomain.com
TINYAUTH_AUTH_USERS=
```
| Variable | Value | Example |
|----------|-------|---------|
| `TINYAUTH_APPURL`{lang=properties} | The public URL you'll use to reach TinyAuth (see exposure below) | `https://tinyauth.mydomain.com` |
| `TINYAUTH_AUTH_USERS`{lang=properties} | The hash generated above | `user:$$2a$$10$$UdLYoJ5lgPsC0RKq...` |
Deploy the stack. The local interface is available at `http://yourserverip:3000`.
### Done !
::
## Exposing TinyAuth with Swag
TinyAuth needs its own subdomain: it's the page users land on before being forwarded to the app they actually want.
::note
We assume you have the subdomain `tinyauth.mydomain.com` with a `CNAME` pointing to `mydomain.com` in your [DNS zone](/general/networking/dns). And of course, [unless you use Cloudflare Zero Trust](/serveex/security/cloudflare), your box's port `443` must be forwarded to your server's port `443` in [NAT rules](/general/networking/nat).
::
::steps{level="3"}
### Add TinyAuth's network to SWAG
Go to Dockge and edit SWAG's compose file by adding TinyAuth's network:
```yaml [compose.yaml]
---
services:
swag:
container_name: # ...
# ...
networks: # Attach container to custom network
# ...
- tinyauth # Name of the declared network
networks: # Define the custom network
# ...
tinyauth: # Declared network name
name: tinyauth_default # Actual external network name
external: true # Marks it as externally defined
```
Redeploy the stack and wait for SWAG to be fully operational.
::note
Here we assume the TinyAuth network name is `tinyauth_default`. You can check the connection by visiting SWAG's dashboard at `http://yourserverip:81`.
::
### Create the subdomain.conf file
In the Swag folders, create the file `tinyauth.subdomain.conf`:
::tip{icon=""}
✨ __Tip:__ Use [File Browser Quantum](/serveex/files/file-browser-quantum) to navigate and edit files instead of using terminal commands.
::
```bash [Terminal]
sudo nano /docker/swag/config/nginx/proxy-confs/tinyauth.subdomain.conf
```
Paste the following configuration:
```nginx [tinyauth.subdomain.conf]
## Version 2023/12/19
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name tinyauth.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
location / {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app tinyauth;
set $upstream_port 3000;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
}
```
Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit.
### Visit your new subdomain
Wait a few minutes, then open `https://tinyauth.mydomain.com` in your browser and log in with the username/password you created above.
::caution
__If it fails:__ check your firewall rules.
::
### Done !
::
## Connecting TinyAuth to Pocket ID
::steps{level="3"}
### Register TinyAuth as an OIDC client
First, [register TinyAuth as an OIDC client in Pocket ID](/serveex/security/pocket-id#registering-an-oidc-client), using this callback URL:
```text
https://tinyauth.mydomain.com/api/oauth/callback/pocketid
```
### Add the Pocket ID provider
Copy the __Client ID__ and __Client Secret__ Pocket ID gives you, then edit TinyAuth's `.env` file:
```bash [Terminal]
sudo nano /docker/tinyauth/.env
```
Add the following:
```properties [.env]
TINYAUTH_OAUTH_PROVIDERS_POCKETID_NAME=Pocket ID
TINYAUTH_OAUTH_PROVIDERS_POCKETID_CLIENTID=
TINYAUTH_OAUTH_PROVIDERS_POCKETID_CLIENTSECRET=
TINYAUTH_OAUTH_PROVIDERS_POCKETID_AUTHURL=https://id.mydomain.com/authorize
TINYAUTH_OAUTH_PROVIDERS_POCKETID_TOKENURL=https://id.mydomain.com/api/oidc/token
TINYAUTH_OAUTH_PROVIDERS_POCKETID_USERINFOURL=https://id.mydomain.com/api/oidc/userinfo
TINYAUTH_OAUTH_PROVIDERS_POCKETID_REDIRECTURL=https://tinyauth.mydomain.com/api/oauth/callback/pocketid
TINYAUTH_OAUTH_PROVIDERS_POCKETID_SCOPES=openid email profile
```
| Variable | Value |
|----------|-------|
| `CLIENTID`{lang=properties} | The client ID copied from Pocket ID |
| `CLIENTSECRET`{lang=properties} | The client secret copied from Pocket ID |
| `AUTHURL` / `TOKENURL` / `USERINFOURL`{lang=properties} | Pocket ID's public URL, with the paths shown above |
Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit.
### Redeploy the stack
Redeploy the TinyAuth stack. On your next visit to `https://tinyauth.mydomain.com`, you'll see a "Login with Pocket ID" option alongside the local login form.
::tip{icon=""}
✨ To skip straight to Pocket ID and hide the local login form, add `TINYAUTH_OAUTH_AUTOREDIRECT=pocketid` to the same `.env` file.
::
### Done !
::
## Protecting an app via reverse proxy
Swag doesn't ship a ready-made include file for TinyAuth, so we'll add the forward-auth check directly to the app's own `*.subdomain.conf`. We'll use Dockge as an example.
::steps{level="3"}
### Open the app's subdomain.conf file
```bash [Terminal]
sudo nano /docker/swag/config/nginx/proxy-confs/dockge.subdomain.conf
```
### Add the forward-auth check
Add an internal `/tinyauth` location, and reference it from the app's `location /` block with `auth_request`:
```nginx [dockge.subdomain.conf]{9-11,25}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name dockge.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
location /tinyauth {
internal;
proxy_pass http://tinyauth:3000/api/auth/nginx;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
}
location @tinyauth_login {
return 302 https://tinyauth.mydomain.com/login?redirect_uri=$scheme://$http_host$request_uri;
}
location / {
auth_request /tinyauth;
error_page 401 = @tinyauth_login;
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app dockge;
set $upstream_port 5001;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
}
```
::note
The `location /tinyauth` block runs inside SWAG's own container, so SWAG needs to be on TinyAuth's Docker network to reach it by name (`tinyauth` here). This should already be set up from [exposing TinyAuth itself](/serveex/security/tinyauth#exposing-tinyauth-with-swag). If you run into an error, double-check SWAG's compose file still has that network attached.
::
Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit.
### Done !
::
That's it! Visiting `https://dockge.mydomain.com` now redirects to TinyAuth first. Repeat this `location /tinyauth` / `auth_request` pattern in any other app's `*.subdomain.conf` to protect it the same way.
::note
Repeat this process for each app you want to protect (unless it has native OIDC support, in which case you can point it directly at Pocket ID instead).
::
## Leaving specific paths public
Sometimes you want most of an app locked behind TinyAuth, but a handful of paths left open, for example a public status page, or the API endpoints a mobile app relies on. Unlike Authentik, TinyAuth has no built-in "authenticated paths" setting for this: it's a plain nginx problem, and it's solved with nginx's own location matching.
A regex `location` block always takes priority over the plain `location /` block, no matter which one appears first in the file. So any path matched by a regex location you define runs its own `proxy_pass`, without ever reaching the `auth_request /tinyauth;` line in `location /`.
For example, to leave Uptime-Kuma's public status page and its assets open while protecting everything else:
```nginx [dockge.subdomain.conf]{9-16}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name stats.*;
include /config/nginx/ssl.conf;
location ~ ^/(status|assets|icon\.svg|api|upload|metrics) {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app uptime-kuma;
set $upstream_port 3001;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
location /tinyauth {
internal;
proxy_pass http://tinyauth:3000/api/auth/nginx;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
}
location @tinyauth_login {
return 302 https://tinyauth.mydomain.com/login?redirect_uri=$scheme://$http_host$request_uri;
}
location / {
auth_request /tinyauth;
error_page 401 = @tinyauth_login;
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app uptime-kuma;
set $upstream_port 3001;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
}
```
::note
Adjust the list of excluded paths to what the app you're protecting actually needs public. Never leave an admin or settings path in that list, only what the app itself documents as safe to expose unauthenticated.
::