---
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.
---


:ellipsis{left=0px width=40rem top=10rem blur=140px zIndex=60}
# TinyAuth

::note
🎯 __Objectives:__

- Install TinyAuth
- Log in via [Pocket ID](/serveex/security/pocket-id) (OIDC)
- Protect an app behind Swag with forward-auth
::

[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, similar in spirit to [Authentik](/serveex/advanced/authentik)'s reverse-proxy mode, but without the rest of Authentik's identity-provider machinery.

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
Folder structure:
```text [Directory tree]
root
└── docker
    └── tinyauth
        ├── compose.yaml
        ├── .env
        └── data
```

Create the data folder:

```bash [Terminal]
sudo mkdir -p /docker/tinyauth/data
```

Generate a password hash for your local account:

```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.
::

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
✨ Add the Watchtower label to automate updates:

```yaml [compose.yaml]
services:
  tinyauth:
    #...
    labels:

      - com.centurylinklabs.watchtower.enable=true
```
::

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`.

## 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).
::

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`.
::

In the Swag folders, create the file `tinyauth.subdomain.conf`:

::tip{icon=""}
✨ __Tip:__ Use [File Browser](/serveex/files/file-browser) 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.

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.
::

## Connecting TinyAuth to Pocket ID
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
```

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 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
✨ To skip straight to Pocket ID and hide the local login form, add `TINYAUTH_OAUTH_AUTOREDIRECT=pocketid` to the same `.env` file.
::

## Protecting an app via reverse proxy
Unlike Authentik, 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.

Open the file:

```bash [Terminal]
sudo nano /docker/swag/config/nginx/proxy-confs/dockge.subdomain.conf
```

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 must be able to reach the TinyAuth container by its Docker name (`tinyauth` here). Add TinyAuth's network to this stack's compose file the same way you did [for Swag](/serveex/security/tinyauth#exposing-tinyauth-with-swag) if it isn't already attached.
::

Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit.

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).
::
