summaryrefslogtreecommitdiff
path: root/docs/handbook/cgit/authentication.md
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-05 17:37:54 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-05 17:37:54 +0300
commit32f5f761bc8e960293b4f4feaf973dd0da26d0f8 (patch)
tree8d0436fdd093d5255c3b75e45f9741882b22e2e4 /docs/handbook/cgit/authentication.md
parent64f4ddfa97c19f371fe1847b20bd26803f0a25d5 (diff)
downloadProject-Tick-32f5f761bc8e960293b4f4feaf973dd0da26d0f8.tar.gz
Project-Tick-32f5f761bc8e960293b4f4feaf973dd0da26d0f8.zip
NOISSUE Project Tick Handbook is Released!
Assisted-by: Claude:Opus-4.6-High Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'docs/handbook/cgit/authentication.md')
-rw-r--r--docs/handbook/cgit/authentication.md288
1 files changed, 288 insertions, 0 deletions
diff --git a/docs/handbook/cgit/authentication.md b/docs/handbook/cgit/authentication.md
new file mode 100644
index 0000000000..a4fe000a87
--- /dev/null
+++ b/docs/handbook/cgit/authentication.md
@@ -0,0 +1,288 @@
+# cgit — Authentication
+
+## Overview
+
+cgit supports cookie-based authentication through the `auth-filter`
+mechanism. The authentication system intercepts requests before page
+rendering and delegates all credential validation to an external filter
+(exec or Lua script).
+
+Source file: `cgit.c` (authentication hooks), `filter.c` (filter execution).
+
+## Architecture
+
+Authentication is entirely filter-driven. cgit itself stores no credentials,
+sessions, or user databases. The auth filter is responsible for:
+
+1. Rendering login forms
+2. Validating credentials
+3. Setting/reading session cookies
+4. Determining authorization per-repository
+
+## Configuration
+
+```ini
+auth-filter=lua:/path/to/auth.lua
+# or
+auth-filter=exec:/path/to/auth.sh
+```
+
+The auth filter type is `AUTH_FILTER` (constant `4`) and receives 12
+arguments.
+
+## Authentication Flow
+
+### Request Processing in `cgit.c`
+
+Authentication is checked in `process_request()` after URL parsing and
+command dispatch:
+
+```c
+/* In process_request() */
+if (ctx.cfg.auth_filter) {
+ /* Step 1: Check current authentication state */
+ authenticate_cookie();
+
+ /* Step 2: Handle POST login attempts */
+ if (ctx.env.request_method &&
+ !strcmp(ctx.env.request_method, "POST"))
+ authenticate_post();
+
+ /* Step 3: Run the auth filter to decide access */
+ cmd->fn(&ctx);
+}
+```
+
+### `authenticate_cookie()`
+
+Opens the auth filter to check the current session cookie:
+
+```c
+static void authenticate_cookie(void)
+{
+ /* Open auth filter with current request context */
+ cgit_open_filter(ctx.cfg.auth_filter,
+ ctx.env.http_cookie, /* current cookies */
+ ctx.env.request_method, /* GET/POST */
+ ctx.env.query_string, /* full query */
+ ctx.env.http_referer, /* referer header */
+ ctx.env.path_info, /* request path */
+ ctx.env.http_host, /* hostname */
+ ctx.env.https ? "1" : "0", /* HTTPS flag */
+ ctx.qry.repo, /* repository name */
+ ctx.qry.page, /* page/command */
+ ctx.env.http_accept, /* accept header */
+ "cookie" /* authentication phase */
+ );
+ /* Read filter's response to determine auth state */
+ ctx.env.authenticated = /* filter exit code */;
+ cgit_close_filter(ctx.cfg.auth_filter);
+}
+```
+
+### `authenticate_post()`
+
+Handles login form submissions:
+
+```c
+static void authenticate_post(void)
+{
+ /* Read POST body for credentials */
+ /* Open auth filter with phase="post" */
+ cgit_open_filter(ctx.cfg.auth_filter,
+ /* ... same 11 args ... */
+ "post" /* authentication phase */
+ );
+ /* Filter processes credentials, may set cookies */
+ cgit_close_filter(ctx.cfg.auth_filter);
+}
+```
+
+### Authorization Check
+
+After authentication, the auth filter is called again before rendering each
+page to determine if the authenticated user has access to the requested
+repository and page:
+
+```c
+static int open_auth_filter(const char *repo, const char *page)
+{
+ cgit_open_filter(ctx.cfg.auth_filter,
+ /* ... request context ... */
+ "authorize" /* authorization phase */
+ );
+ int authorized = cgit_close_filter(ctx.cfg.auth_filter);
+ return authorized == 0; /* 0 = authorized */
+}
+```
+
+## Auth Filter Arguments
+
+The auth filter receives 12 arguments in total:
+
+| # | Argument | Description |
+|---|----------|-------------|
+| 1 | `filter_cmd` | The filter command itself |
+| 2 | `http_cookie` | Raw `HTTP_COOKIE` header value |
+| 3 | `request_method` | HTTP method (`GET`, `POST`) |
+| 4 | `query_string` | Raw query string |
+| 5 | `http_referer` | HTTP Referer header |
+| 6 | `path_info` | PATH_INFO from CGI |
+| 7 | `http_host` | Hostname |
+| 8 | `https` | `"1"` if HTTPS, `"0"` if HTTP |
+| 9 | `repo` | Repository URL |
+| 10 | `page` | Page/command name |
+| 11 | `http_accept` | HTTP Accept header |
+| 12 | `phase` | `"cookie"`, `"post"`, or `"authorize"` |
+
+## Filter Phases
+
+### `cookie` Phase
+
+Called on every request. The filter should:
+1. Read the session cookie from argument 2
+2. Validate the session
+3. Return exit code 0 if authenticated, non-zero otherwise
+
+### `post` Phase
+
+Called when the request method is POST. The filter should:
+1. Read POST body from stdin
+2. Validate credentials
+3. If valid, output a `Set-Cookie` header
+4. Output a redirect response (302)
+
+### `authorize` Phase
+
+Called after authentication to check per-repository access. The filter
+should:
+1. Check if the authenticated user can access the requested repo/page
+2. Return exit code 0 if authorized
+3. Return non-zero to deny access (cgit will show an error page)
+
+## Filter Return Codes
+
+| Exit Code | Meaning |
+|-----------|---------|
+| 0 | Success (authenticated/authorized) |
+| Non-zero | Failure (unauthenticated/unauthorized) |
+
+## Environment Variables
+
+The auth filter also has access to standard CGI environment variables:
+
+```c
+struct cgit_environment {
+ const char *cgit_config; /* $CGIT_CONFIG */
+ const char *http_host; /* $HTTP_HOST */
+ const char *https; /* $HTTPS */
+ const char *no_http; /* $NO_HTTP */
+ const char *http_cookie; /* $HTTP_COOKIE */
+ const char *request_method; /* $REQUEST_METHOD */
+ const char *query_string; /* $QUERY_STRING */
+ const char *http_referer; /* $HTTP_REFERER */
+ const char *path_info; /* $PATH_INFO */
+ const char *script_name; /* $SCRIPT_NAME */
+ const char *server_name; /* $SERVER_NAME */
+ const char *server_port; /* $SERVER_PORT */
+ const char *http_accept; /* $HTTP_ACCEPT */
+ int authenticated; /* set by auth filter */
+};
+```
+
+## Shipped Auth Filter
+
+cgit ships a Lua-based hierarchical authentication filter:
+
+### `filters/simple-hierarchical-auth.lua`
+
+This filter implements path-based access control using a simple user
+database and repository permission map.
+
+Features:
+- Cookie-based session management
+- Per-repository access control
+- Hierarchical path matching
+- Password hashing
+
+Usage:
+
+```ini
+auth-filter=lua:/usr/lib/cgit/filters/simple-hierarchical-auth.lua
+```
+
+## Cache Interaction
+
+Authentication affects cache keys. The cache key includes the
+authentication state and cookie:
+
+```c
+static const char *cache_key(void)
+{
+ return fmt("%s?%s?%s?%s?%s",
+ ctx.qry.raw,
+ ctx.env.http_host,
+ ctx.env.https ? "1" : "0",
+ ctx.env.authenticated ? "1" : "0",
+ ctx.env.http_cookie ? ctx.env.http_cookie : "");
+}
+```
+
+This ensures that:
+- Authenticated and unauthenticated users get separate cache entries
+- Different authenticated users (different cookies) get separate entries
+- The cache never leaks restricted content to unauthorized users
+
+## Security Considerations
+
+1. **HTTPS**: Always use HTTPS when authentication is enabled to protect
+ cookies and credentials in transit
+2. **Cookie flags**: Auth filter scripts should set `Secure`, `HttpOnly`,
+ and `SameSite` cookie flags
+3. **Session expiry**: Implement session timeouts in the auth filter
+4. **Password storage**: Never store passwords in plain text; use bcrypt or
+ similar hashing
+5. **CSRF protection**: The auth filter should implement CSRF tokens for
+ POST login forms
+6. **Cache poisoning**: The cache key includes auth state, but ensure the
+ auth filter is deterministic for the same cookie
+
+## Disabling Authentication
+
+By default, no auth filter is configured and all repositories are publicly
+accessible. To restrict access, set up the auth filter and optionally
+combine with `strict-export` for file-based visibility control.
+
+## Example: Custom Auth Filter (Shell)
+
+```bash
+#!/bin/bash
+# Simple auth filter skeleton
+PHASE="${12}"
+
+case "$PHASE" in
+ cookie)
+ COOKIE="$2"
+ if validate_session "$COOKIE"; then
+ exit 0 # authenticated
+ fi
+ exit 1 # not authenticated
+ ;;
+ post)
+ read -r POST_BODY
+ # Parse username/password from POST_BODY
+ # Validate credentials
+ # Set cookie header
+ echo "Status: 302 Found"
+ echo "Set-Cookie: session=TOKEN; HttpOnly; Secure"
+ echo "Location: $6"
+ echo
+ exit 0
+ ;;
+ authorize)
+ REPO="$9"
+ # Check if current user can access $REPO
+ exit 0 # authorized
+ ;;
+esac
+```