Skip to content

melkishengue/auth-dokploy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Securing a Golang Application with Zitadel, Traefik, and Tinyauth

A complete guide to implementing forward authentication with role-based access control for a Golang application using Zitadel (Identity Provider), Tinyauth (Forward Auth Middleware), and Traefik (Reverse Proxy).

Overview

This setup demonstrates how to secure a Golang web application using:

  • Zitadel: Open-source identity and access management (IAM) platform that handles user authentication
  • Tinyauth: Lightweight forward authentication middleware that integrates with reverse proxies
  • Traefik: Modern reverse proxy that routes requests and enforces authentication
  • Golang App: Simple application that displays authentication headers to verify the setup

Purpose: Local development and testing environment to showcase secure authentication patterns.

Note: This setup uses HTTP for local testing only. For production environments, HTTPS/TLS should be configured.

Architecture

User Request → Traefik → Tinyauth (checks auth) → Zitadel (if not authenticated)
                    ↓                                      ↓
              Golang App ← Headers (user, email, groups) ← Access Token

Flow:

  1. User accesses APP_DOMAIN
  2. Traefik intercepts the request and forwards it to Tinyauth
  3. Tinyauth checks if the user has a valid session
  4. If not authenticated, Tinyauth redirects to Zitadel login
  5. User logs in with Zitadel
  6. Zitadel returns OAuth tokens with user information and roles
  7. Tinyauth creates a session and forwards authentication headers to the app
  8. The Golang app receives headers: Remote-User, Remote-Email, Remote-Groups
sequenceDiagram
    participant U as User
    participant T as Traefik
    participant Ta as Tinyauth (Auth Proxy)
    participant Z as Zitadel (Identity Provider)
    participant A as Golang App

    title User Authentication and Request Flow

    U->>T: 1. Accesses APP_DOMAIN
    T->>Ta: 2. Intercepts request and forwards (APP_DOMAIN)

    Ta->>Ta: 3. Check for valid session/cookie

    alt Not Authenticated
        Ta->>U: 4. Redirect to Zitadel Login
        U->>Z: 5. User logs in/authenticates
        Z-->>U: 6. Returns OAuth Tokens (Code/Token)
        U->>Ta: 7. Presents Tokens for session validation
        Ta->>Z: 7. Validate token (UserInfo / Introspection)
        Z-->>Ta: User Info (Email, Groups)
        Ta->>Ta: 8. Create session/cookie
    end

    Ta->>A: 8. Forward request with Authentication Headers
    Note right of Ta: Headers: Remote-User, Remote-Email, Remote-Groups
    A-->>U: 9. Serve content (Authenticated)

Loading

How to add authentication to a new application

Securing a new app involves the following 2 steps:

Create docker-compose file

Protecting a new app involves the following steps:

  • create a new docker compose file
  • create an oauth application in Zitadel if required
  • create a new tinyauth instance if required

New docker compose service:

Minimal setup:

services:
  myapp:
    build: .
    restart: unless-stopped
    container_name: myapp
    expose:
      - APP_PORT
    labels:
      traefik.enable: true
      traefik.http.routers.myapp.rule: Host(`${APP_DOMAIN}`)
      traefik.http.routers.myapp.entrypoints: websecure
      traefik.http.routers.myapp.tls: true
      traefik.http.routers.myapp.tls.certresolver: letsencrypt
      traefik.http.routers.myapp.priority: 100
      traefik.http.routers.myapp.middlewares: tinyauth-forward@docker
      traefik.http.routers.myapp.service: myapp
      traefik.http.services.myapp.loadbalancer.server.port: APP_PORT
    networks:
      - dokploy-network
networks:
  dokploy-network:
    external: true

This config uses the default tinyauth melkishengue-hq instance. This is fine if you don't need a oauth2 application specific for your app (you just need to authenticate users). If you need a separate app for the new service follow the next steps.

New oauth2 application

Now to go (Zitadel)[https://login-dailylesson.melkishengue.com/api/oauth/callback/zitadel] to create a new application. Choose the organization and the project where the application should belong to and click on NEW. Next:

  • enter the name of the application. E.g. dailylesson
  • select WEB and continue
  • choose CODE to have auth code exchange flow and continue
  • enter the following redirect url: https://login-dailylesson.melkishengue.com/api/oauth/callback/zitadel. This url should match the PROVIDERS_ZITADEL_REDIRECT_URL env var configured in tinyauth docker compose configuration. Enter your apps url for the logout url, e.g. https://dailylesson.melkishengue.com.
  • next click create an copy the client id and client secret and add them to the dockerfile in the next step

New tinyauth instance

Create a new tinyauth instance with the following minimal configuration:

services:
  myapp:
    build: .
    restart: unless-stopped
    container_name: myapp
    expose:
      - APP_PORT
    environment:
      # ... your envs
    labels:
      traefik.enable: true
      traefik.http.routers.myapp.rule: Host(`${APP_DOMAIN}`)
      traefik.http.routers.myapp.entrypoints: websecure
      traefik.http.routers.myapp.tls: true
      traefik.http.routers.myapp.tls.certresolver: letsencrypt
      traefik.http.routers.myapp.priority: 100
      traefik.http.routers.myapp.middlewares: myapp-tinyauth-forward@docker
      traefik.http.routers.myapp.service: myapp
      traefik.http.services.myapp.loadbalancer.server.port: APP_PORT
    networks:
      - dokploy-network

  # oauth2 login screen
  myapp-tinyauth:
    image: ghcr.io/steveiliop56/tinyauth:latest
    container_name: myapp-tinyauth
    restart: unless-stopped
    expose:
      - 3000
    environment:
      SECRET: ${TINYAUTH_SECRET_KEY}
      APP_URL: https://${TINYAUTH_DOMAIN}
      PORT: 3000
      LOG_LEVEL: trace
      LOG_JSON: true
      SECURE_COOKIE: true
      OAUTH_AUTO_REDIRECT: zitadel
      OAUTH_REDIRECT_URI: https://${APP_DOMAIN}
      PROVIDERS_ZITADEL_CLIENT_ID: ${PROVIDERS_ZITADEL_CLIENT_ID}
      PROVIDERS_ZITADEL_CLIENT_SECRET: ${PROVIDERS_ZITADEL_CLIENT_SECRET}
      PROVIDERS_ZITADEL_AUTH_URL: https://${ZITADEL_DOMAIN}/oauth/v2/authorize
      PROVIDERS_ZITADEL_TOKEN_URL: https://${ZITADEL_DOMAIN}/oauth/v2/token
      PROVIDERS_ZITADEL_USER_INFO_URL: https://${ZITADEL_DOMAIN}/oidc/v1/userinfo
      PROVIDERS_ZITADEL_REDIRECT_URL: https://${TINYAUTH_DOMAIN}/api/oauth/callback/zitadel
      PROVIDERS_ZITADEL_SCOPES: openid profile email groups
      PROVIDERS_ZITADEL_NAME: Zitadel
      BACKGROUND_IMAGE: https://images.pexels.com/photos/3038813/pexels-photo-3038813.jpeg
      APP_TITLE: myapp
      USERS_ALLOW_ANY_OAUTH: true
    labels:
      traefik.enable: true
      traefik.http.routers.myapp-tinyauth.rule: Host(`${TINYAUTH_DOMAIN}`)
      traefik.http.routers.myapp-tinyauth.entrypoints: websecure
      traefik.http.routers.myapp-tinyauth.tls: true
      traefik.http.routers.myapp-tinyauth.tls.certresolver: letsencrypt
      traefik.http.services.myapp-tinyauth.loadbalancer.server.port: 3000
      traefik.http.middlewares.myapp-tinyauth-forward.forwardauth.address: http://myapp-tinyauth:3000/api/auth/traefik
      traefik.http.middlewares.myapp-tinyauth-forward.forwardauth.trustForwardHeader: true
      traefik.http.middlewares.myapp-tinyauth-forward.forwardauth.authResponseHeaders: remote-user,remote-email,remote-groups,remote-user-id,remote-name,Authorization
      traefik.http.middlewares.myapp-tinyauth-forward.forwardauth.authRequestHeaders: Cookie,Authorization,Accept,X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Proto,X-Forwarded-Uri,X-Real-Ip
    depends_on:
      - myapp
    networks:
      - dokploy-network
networks:
  dokploy-network:
    external: true

Reference values:

TINYAUTH_DOMAIN=login-dailylesson.melkishengue.com
APP_DOMAIN=dailylesson.melkishengue.com
ZITADEL_DOMAIN=auth.afribytes.com

PROVIDERS_ZITADEL_CLIENT_ID=
PROVIDERS_ZITADEL_CLIENT_SECRET=

TINYAUTH_SECRET_KEY=

Note: TINYAUTH_DOMAIN and APP_DOMAINneeds to have to have the same sub-domain otherwise the redirect to APP_DOMAIN will fail because of security reason on the frontend.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors